为啥基类方法不尊重 new 关键字?

Posted

技术标签:

【中文标题】为啥基类方法不尊重 new 关键字?【英文标题】:Why isn't the new keyword respected by base class methods?为什么基类方法不尊重 new 关键字? 【发布时间】:2020-10-11 15:00:38 【问题描述】:

我正在制作一个复杂的纸牌游戏。所以我有一个类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,而不是标有new 关键字的TwinCard 版本。所以它试图使用基类的所谓隐藏的effects,而不是new List&lt;Effect&gt; 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 根据这些侦听器更新自己的值。但这是一个庞大的听众网络。

【问题讨论】:

你知道new在属性隐藏方面是如何工作的吗? 我认为您实际上想要virtualoverridenew 不适用于多态性。 感谢cmets,我得到了Gusman的答复。干杯! 【参考方案1】:

您误解了 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 属性。

【讨论】:

非常感谢您的回答和清晰!那么,您是否建议我将 Card 类的每个属性都设为虚拟?我可以这样做,但我希望继承类中的所有特殊代码,因为这是特殊效果,而不是转换整个基类以适应特殊情况。 @JonathanFryxelius virtual 表现出您认为new 工作的方式,您不必强制覆盖virtual 方法或属性,因此您可以将所有属性标记为virtual,把您在基类上的逻辑并在派生类中仅覆盖您需要修改的属性。此外,您可以将您的方法设置为virtual 以在派生类中覆盖它们,以防您需要针对特殊情况的特殊逻辑。只有abstract 方法或属性被强制在派生类中被覆盖。

以上是关于为啥基类方法不尊重 new 关键字?的主要内容,如果未能解决你的问题,请参考以下文章

强制方法从基类调用子类新方法

C# 中基类,虚类,抽象类,密封类,接口的区别

java里面为啥总要new,不new为啥不行?scanner scancer=new scan

c++中为啥要用new 函数()实现多态?

类和继承

123