Почему новое ключевое слово не соблюдается методами базового класса?
Я делаю сложную карточную игру. Итак, у меня есть класс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();
}
Показательный пример: у меня есть карта под названием "Концентрат" с 1 эффектом в ее списке. У меня есть TwinCard с исходной настройкой на концентрацию. В режиме отладки (Visual Studio) я вижу 2 поля с именем "эффекты", одно пустое, а другое имеет эффект, указанный в Concentrate. Когда я ввожу GetParsedEffects() на TwinCard, он показывает эффекты как 0 в списке, предположительно, он использует одно из полей эффектов, но не другое.
Если это ошибка C#, как мне ее обойти? Если это ожидаемое поведение, как бы я иначе его спроектировал? Единственный другой вариант, который я вижу, - это реализовать события и прослушиватели для изменений в каждом атрибуте, и чтобы TwinCard обновляла свои собственные значения в зависимости от этих прослушивателей. Но это гигантская сеть слушателей.
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);
Как вы думаете, что он напечатает?
Если вы подумали "базовая карта новая карта"... ну, вы ошибаетесь, она покажет "базовая карта базовая карта", потому что мы используем тип Card
вместо того NewCard
поэтому этот код не "видит" new
свойство.