3

我正在尝试创建从通用卡片类继承的各种卡片的表示形式,并且所有卡片都包含对其拥有的牌组的引用。

我尝试按照此处的建议重新声明它们,但它仍然不会转换为特定的卡类型。

我目前拥有的代码是这样的:

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 类型系统中优雅地完成吗?如果是这样,可以提供一些例子吗?

4

3 回答 3

3

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

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

您的FooCard课程将如下所示:

public class FooCard : Card<FooCard>

与您当前代码相比的一个优势可能是您不必重新声明该OwningDeck属性;它是自动输入Deck<FooCard>FooCard

于 2012-09-23T21:41:23.130 回答
2

你在这里遇到的是一个常见的设计错误,你试图将属性“OwningDeck”专门化为 FooCard,这是“隐藏”“OwningDeck”的基本声明。您所做的选择,将“OwningDeck”放在 Card 类中,旨在确保所有类型的 Card 都具有 OwningDeck 属性 - 这也意味着该属性必须是最低公分母类型(即“Deck ')。

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

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

至于更好的实现,我看到 OR Mapper在实际实现中击败了我。他说了什么;)

于 2012-09-23T21:43:21.810 回答
2

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>
{
}

原始代码不起作用的原因是 aDeck<Card>与 a 不同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类。

于 2012-09-23T21:45:15.350 回答