Как переопределить, а не скрыть переменную-член (поле) в подклассе 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; а также позволял всем предметам знать, что у других предметов есть статистика.

Надеюсь, это будет полезно кому-то еще :)

Другие вопросы по тегам