我正在尝试创建各种类型的卡的表示,这些卡继承自通用卡类,并且都包含对其所属牌组的引用。
正如这里所建议的,我尝试重新声明它们,但它仍然无法转换为特定的卡类型。
我目前拥有的代码如下:
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
类。