我正在制作一个复杂的纸牌游戏。所以我有一个类Card
,它具有特定于该游戏的一些属性,例如List<Effect> effects
。该类还有一些基本方法将其效果解析为可读的英语,GetParsedEffects()
通过列表工作并将它们解析为字符串。
public class Card {
public List<Effect> effects;
public string GetParsedEffects() {
foreach(Effect e in effects)
//formatting stuff
}
}
现在我决定要创建一个非常特殊的效果,你可以创建一个相同的卡片双胞胎。当我说同卵双胞胎时,我的意思是一张卡总是与另一张卡具有相同的值,当它们中的任何一个更新一个值时,两者都会受到影响。这就像有 2 张同一张牌的实例(但它们不可能完全相同,因为当您玩其中一张时,我需要知道它是哪一张,这样它们就不会都进入丢弃堆。
我尝试通过使用继承来做到这一点。所以我创建了一个子类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<Effect> effects
。
我的印象是new
关键字意味着任何和所有调用都将交换为new
版本,但看起来情况并非如此。是我做错了什么,还是误解了new
的工作原理?
我认为这与我迭代手牌的方式有关,将牌定义为Card
;
foreach(Card c in hand) {
c.GetParsedEffect();
}
举个例子: 我有一张名为"浓缩"的卡,其列表中有1种效果。 我有一张双胞胎卡,它的原始套装是集中。 在调试模式(Visual Studio(下,我可以看到2个名为"效果"的字段,一个为空,另一个具有Focus中引用的效果。 当我在 TwinCard 上输入 GetParsedEffects(( 时,它在列表中将效果显示为 0,据说它使用其中一个效果字段,而不是另一个。
如果这是一个 C# 错误,我将如何解决它? 如果这是预期行为,否则我将如何设计它?我看到的唯一其他选择是为每个属性的更改实现事件和侦听器,并让 TwinCard 根据这些侦听器更新自己的值。但那是一个巨大的听众网络。
你误解了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);
你认为它会打印什么?
如果您认为"底卡新卡"...好吧,你错了,它会显示"基卡底卡",因为我们使用的是类型Card
而不是NewCard
,所以这段代码不会"看到"new
属性。