为什么基本 class 方法不遵守 new 关键字?

Why isn't the new keyword respected by base class methods?

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

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

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

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

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。由于这是一个基本 class 方法,它似乎使用基本 class 字段 effects 而不是标有 new 关键字的 TwinCard 版本。所以它试图使用据称隐藏的 effects 基数 class,而不是 new List<Effect> effects.

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

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

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

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

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

您误解了 new 关键字。 new 从使用 class 的代码中隐藏了原始成员,而不是从基础 class 中隐藏了原始成员。您想要的是 virtual,在 class 中覆盖的 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 所以此代码不 'sees' new 属性.