0

我正在制作一个复杂的纸牌游戏。所以我有一个类Card,它有一些特定于这个游戏的属性,例如List<Effect> effects. 该类还有一些基本方法可以将其效果解析为可读的英语,GetParsedEffects()这些方法通过列表工作并将它们解析为字符串。

public class Card {
   public List<Effect> effects;
   public string GetParsedEffects() {
      foreach(Effect e in effects)
         //formatting stuff
   }
}

现在我决定要创建一个非常特殊的效果,您可以在其中创建一张卡片的同卵双胞胎。当我说同卵双胞胎时,我的意思是一张卡片总是与另一张卡片具有相同的值,并且当它们中的任何一个更新值时,两者都会受到影响。这就像有两张同一张牌(但它们不可能完全相同,因为当你打出其中一张时,我需要知道是哪一张,这样它们就不会进入弃牌堆。)

我试图通过使用继承来做到这一点。所以我创建了一个子类TwinCard : Card。TwinCard 有一个附加属性 ,Card original并在所有属性上使用new关键字,以便它们获取和设置原始卡的属性而不是它自己的属性。所以基本上,你有一张原始卡,然后是只反映原始卡的双卡,对双卡的更改会影响原始卡。由于两者都Card作为类共享,因此所有方法都可以使用它们。

public class TwinCard : Card {
   public Card original;
   public new List<Effect> effects { get { return original.effects;} set { original.effects = value; } }

   public TwinCard(Card original) {
      this.original = original;
   }
}

我对自己很满意。

直到我试图跟GetParsedEffects()注所有手牌,包括 TwinCard。由于这是一个基类方法,它似乎使用基类字段effects而不是用关键字标记的 TwinCard 版本new。所以它试图使用所谓隐藏effects的基类,而不是new List<Effect> effects.

我的印象是new关键字意味着任何和所有调用都将交换new版本,但看起来情况并非如此。我做错了什么,还是我误解了它的new工作原理?

我认为这与我遍历手牌的方式有关,将卡片定义为Card;

foreach(Card c in hand) {
   c.GetParsedEffect();
}

举个例子:我有一张名为 Concentrate 的卡片,它的列表中有 1 个效果。我有一张 TwinCard,其原始设置为 Concentrate。在调试模式 (Visual Studio) 中,我可以看到 2 个名为“效果”的字段,一个为空,另一个具有 Concentrate 中引用的效果。当我在 TwinCard 上输入 GetParsedEffects() 时,它在列表中显示效果为 0,据说它使用了一个效果字段,而不是另一个。

如果这是一个 C# 错误,我将如何解决它?如果这是预期的行为,我将如何设计它?我看到的唯一其他选择是为每个属性的更改实现事件和侦听器,并让 TwinCard 根据这些侦听器更新自己的值。但这是一个庞大的听众网络。

4

1 回答 1

3

您误解了new关键字。new对使用该类的代码而不是基类隐藏原始成员。你想要的是virtual,一个virtual在类中被覆盖的成员替换了基础成员。看这个例子:

public class Card
{
    public virtual string eff { get; set; } = "basecard";

    public void PrintEffect()
    {
        Console.WriteLine(eff);
    }
}

public class NewCard : Card
{
    public new string eff { get; set; } = "newcard";
}

public class VirtualCard : Card
{
    public override string eff { get; set; } = "virtualcard";
}

public static void Main()
{
    Card c = new Card();
    c.PrintEffect();
    Console.WriteLine(c.eff);
    NewCard cs = new NewCard();
    cs.PrintEffect();
    Console.WriteLine(cs.eff);
    VirtualCard cv = new VirtualCard();
    cv.PrintEffect();
    Console.WriteLine(cv.eff);
}

你会得到一个结果

basecard
basecard
basecard
newcard
virtualcard
virtualcard

如您所见,该new属性仅对使用显式类型的代码可见NewCard,但virtual新代码和基本代码可见。

此外,要演示它,请检查以下内容:

Card c = new NewCard();
c.PrintEffect();
Console.WriteLine(c.eff);

你认为它会打印什么?

如果你认为“basecard newcard”......好吧,你错了,它会显示“basecard basecard”,因为我们使用的是类型Card而不是NewCard所以这段代码不会“看到”new属性。

于 2020-06-21T18:40:04.703 回答