Как переопределить, а не скрыть переменную-член (поле) в подклассе C#?
Я хочу, чтобы это сообщило мне Имя как ItemA, так и ItemB. Он должен сказать мне "Subitem \ nSubitem", но вместо этого он говорит мне "Item \ nSubitem". Это связано с тем, что переменная "Имя", определенная в классе Subitem, технически является переменной, отличной от переменной "Имя", определенной в базовом классе Item. Я хочу изменить исходную переменную на новое значение. Я не понимаю, почему это не самая легкая вещь, учитывая виртуальную и переопределенную работу с методами. Я был совершенно не в состоянии выяснить, как на самом деле переопределить переменную.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Example
{
class Program
{
static void Main(string[] args)
{
System.Console.WriteLine(ItemA.Name);
System.Console.WriteLine(ItemB.Name);
System.Console.ReadKey();
}
static Item ItemA = new Subitem();
static Subitem ItemB = new Subitem();
}
public class Item
{
public string Name = "Item";
}
public class Subitem : Item
{
new public string Name = "Subitem";
}
}
6 ответов
Вы не можете переопределить переменные в C#, но вы можете переопределить свойства:
public class Item
{
public virtual string Name {get; protected set;}
}
public class Subitem : Item
{
public override string Name {get; protected set;}
}
Другой подход заключается в изменении значения в подклассе, например так:
public class Item
{
public string Name = "Item";
}
public class Subitem : Item
{
public Subitem()
{
Name = "Subitem";
}
}
Там нет понятия полиморфизма (что делает override
делать свое дело) с полями (что вы называете переменными). Вместо этого вам нужно использовать свойство.
public class Item
{
public virtual string Name
{
get { return "Item"; }
}
}
public class SubItem : Item
{
public override string Name
{
get { return "Subitem"; }
}
}
Этот вопрос довольно старый, и автор, вероятно, использовал C# 5.0 или ниже. Но у меня возник тот же вопрос, и я нашел очень удобное решение, которое работает с C# 6.0 и выше, которым я хочу поделиться с вами:
C# 6.0 дает возможность инициализировать авто-свойства. Так что можно использовать только это:
public class Item
{
public virtual string Name { get; set; } = "Item";
}
public class Subitem : Item
{
public override string Name { get; set; } = "Subitem";
}
Что будет означать "переопределение переменной"?
Переменная - это именованное местоположение в концептуальной памяти ("концептуальная", потому что это может быть регистр, хотя и не очень вероятно с полями).
Переопределение метода заменяет один набор операций другим.
Поскольку именованное место в памяти не является набором операций, как вы можете его заменить? Какие вещи, которые ожидают использовать это именованное место в памяти, должны делать?
Если вы хотите изменить то, что хранится в памяти, просто сделайте это при создании производного класса:
public class Item
{
public string Name = "Item";
}
public class Subitem : Item
{
public SubItem()
{
Name = "Subitem";
}
}
Если вы хотите переопределить как набор операций, то определите набор операций (метод или свойство) для переопределения.
Возможное решение - использовать свойство и переопределить его метод получения, например так:
public class Item
{
public virtual string Name { get { return "Item"; } }
}
public class Subitem : Item
{
public override string Name { get { return "Subitem"; } }
}
У меня была похожая проблема: переменные-члены сами были объектами класса, унаследованными друг от друга. После долгих исследований и размышлений я нашел хорошее для меня решение. Здесь я делюсь этим в надежде, что это поможет кому-то еще. (И да, я понимаю, что это старый пост, но поскольку он был наверху моих поисков в Google...)
Моя цель заключалась в том, чтобы Unity показывала в редакторе конкретный класс статистики для каждой категории предметов. Другими словами, я хотел, чтобы при выборе объекта Weapon отображались данные WeaponStats, а при выборе объекта Armor отображались показатели ArmorStats, при этом у Weapon и Armor были общие переменные, а у WeaponStats и ArmorStats также были общие переменные.
В частности, я пытался использовать ключевое слово new, чтобы базовая переменная (типа класса) могла быть переопределена переменной производного класса (типа, производного от класса базовой переменной). Как в:
public class ItemStats {
// variables like size, weight, etc
}
public class WeaponStats : ItemStats {
// Additional variables like damage, rate of fire, etc
}
public class ArmorStats : ItemStats {
// Additional variables like degree of damage reduction, etc
public class Item {
public ItemStats stats;
protected float degredation;
// etc.
}
public class Weapon : Item {
public new WeaponStats stats; // *The problem*
protected Ammo ammo;
// etc
}
public class Armor : Item {
public new ArmorStats stats; // *Same problem*
// etc
}
Однако на самом деле это не скрыло базовую переменную ItemStats, а вместо этого скрыло новую переменную WeaponStats. А также создать много головной боли по поводу того, какие переменные характеристики имеются в виду в классе оружия. tldr, это была плохая идея.
Лучшее решение, которое я наконец придумал, узнав гораздо больше о наследовании, заключалось в следующем:
// Make the base class abstract, so each item has its own subclass.
// This avoids having two variables for stats, the problem I was trying to avoid.
public abstract class Item {
public abstract ItemStats Stats();
// notice that there is no ItemStats variable here anymore
protected float degredation;
// etc.
}
public class Weapon : Item {
public WeaponStats stats;
protected Ammo ammo;
// This function lets other items interface with its stats.
public override ItemStats Stats() {
return stats;
}
// etc
}
// The same idea for Armor as for Weapon
Это позволило редактору Unity отображать только соответствующий класс статистики (например, WeaponStats с оружием и ArmorStats с броней); дал каждому классу Stats унаследованные переменные, общие для всех ItemsStats; а также позволял всем предметам знать, что у других предметов есть статистика.
Надеюсь, это будет полезно кому-то еще :)