如何在 C# 中使用类的类型作为继承集合属性的类型参数



我正在尝试创建各种类型的卡的表示,这些卡继承自通用卡类,并且都包含对其所属牌组的引用。

正如这里所建议的,我尝试重新声明它们,但它仍然无法转换为特定的卡类型。

我目前拥有的代码如下:

public class Deck<T> : List<T> 
where T : Card 
{
void Shuffle()
{
throw new NotImplementedException("Shuffle not yet implemented.");
}
}
public class Card
{
public Deck<Card> OwningDeck { get; set; }
}
public class FooCard : Card
{
public Deck<FooCard> OwningDeck
{
get
{
return (Deck<FooCard>)base.OwningDeck;
}
set
{
OwningDeck = value;
}
}
}

我得到的编译时错误:错误2无法将类型Game.Cards.Deck<Game.Cards.Card>转换为Game.Cards.Deck<Game.Cards.FooCard>

还有一条警告,建议我使用一个新的运算符来指定隐藏是故意的。这样做会违反惯例吗?有更好的方法吗?

我向stackoverflow提出的问题是:我想做的事情能在中优雅地完成吗。NET类型的系统?如果是,能否提供一些例子

您可以为您的卡配备一个通用参数,该参数指定您正在使用的卡的基类:

public class Card<TCard>
where TCard : Card<TCard>
{
public Deck<TCard> OwningDeck { get; set; }
}

你的FooCard类会是这样的:

public class FooCard : Card<FooCard>

与当前代码相比,一个优点可能是不必重新声明OwningDeck属性;它在CCD_ 6中自动地为CCD_。

您在这里遇到的是一个常见的设计错误,即您试图将属性"OwningDeck"专门化为FooCard,这"隐藏"了"Owning Deck"的基声明。您选择将"OwningDeck"放在Card类中,是为了确保所有类型的Card都具有OwningDeck属性,也就是说,该属性必须是最低公分母类型(即"Deck")。

因此,您不能在派生类型(即FooCard)中重新声明此属性,因为您正试图更改属性的类型-这是一个糟糕的设计选择-而在.net.中实际上并不支持

收到警告的原因是,属性Deck<FooCard> OwningDeck隐藏属性Deck<Card> OwningDeck。如果有人将变量强制转换为类型Card,然后访问OwningDeck属性,他们将从OwningDeck获取该属性。但是,如果他们将其强制转换为FooCard,然后访问相同的属性,他们将获得从Deck<FooCard> OwningDeck返回的值。编译器不知道这些可能正在访问同一个变量,但这是非常危险的,因为程序员不会期望你的代码有这种行为。

至于更好的实现,我认为O.R.Mapper已经在实际实现中击败了我。他说的话;)

如果您愿意使Card类具有通用性,那么您可以实现与您想要的接近的东西,如下所示:

public class Deck<T> : List<Card<T>> 
where T : Card<T>
{
void Shuffle()
{
throw new NotImplementedException("Shuffle not yet implemented.");
}
}
public class Card<T> where T : Card<T>
{
public Deck<T> OwningDeck { get; set; }
}
public class FooCard : Card<FooCard>
{
}

原始代码不起作用的原因是Deck<Card>Deck<FooCard>不同,反之亦然。想象一下:

Deck<Card> deck = new Deck<Card>();
deck.Add(new BarCard());
Deck<FooCard> fooDeck = (Deck<FooCard>)deck;  // What should happen here?
FooCard foo = deck[0];                        // or here?

所以你需要问问自己,"为什么Card类关心哪一副牌拥有它?"在我所知道的大多数程序中,卡片没有理由知道这个细节。如果你想让它了解Deck的具体原因,那么它真正需要了解多少Deck?例如,一副牌组有可能持有各种不同Deck类型的牌吗?当你从牌组中取出卡片时,你真的需要知道它们与你当前的卡片类型完全相同吗?

一旦回答了这样的问题,您就可以更好地决定OwningDeck是否应该是Deck<T>,或者Deck<T>实现的某种协变或逆变接口,或者它是否属于Card类。

最新更新