Почему я не могу использовать виртуальный / переопределение на переменных класса, как я могу на методах?

В следующем примере я в состоянии создать виртуальный метод Show() в наследованном классе и затем переопределяют его в наследующем классе.

Я хочу сделать то же самое с переменной защищенного класса prefix но я получаю ошибку:

'Виртуальный' модификатор не допустим для этого объекта

Но так как я не могу определить эту переменную как виртуальную / переопределение в моих классах, я получаю предупреждение компилятора:

TestOverride234355. SecondaryTransaction.prefix' скрывает наследованного участника 'TestOverride234355. Transaction.prefix'. Используйте новое ключевое слово, если сокрытие было предназначено.

К счастью, когда я добавляю new ключевое слово все хорошо работает, который в порядке, так как я получаю ту же функциональность, но это поднимает два вопроса:

  1. Почему я могу использовать виртуальный / переопределение для методов, но не для переменных защищенного класса?

  2. Каково различие на самом деле между виртуальным подходом / подходом переопределения и подходом hide-it-with-new с тех пор, по крайней мере, в этом примере, который они предлагают той же функциональности?

Код:

using System;

namespace TestOverride234355
{
    public class Program
    {
        static void Main(string[] args)
        {
            Transaction st1 = new Transaction { Name = "name1", State = "state1" };
            SecondaryTransaction st2 = 
                new SecondaryTransaction { Name = "name1", State = "state1" };

            Console.WriteLine(st1.Show());
            Console.WriteLine(st2.Show());

            Console.ReadLine();
        }
    }

    public class Transaction
    {
        public string Name { get; set; }
        public string State { get; set; }

        protected string prefix = "Primary";

        public virtual string Show()
        {
            return String.Format("{0}: {1}, {2}", prefix, Name, State);
        }
    }

    public class SecondaryTransaction : Transaction
    {
        protected new string prefix = "Secondary";

        public override string Show()
        {
            return String.Format("{0}: {1}, {2}", prefix, Name, State);
        }
    }
}
13
задан Edward Tanguay 4 March 2010 в 10:21
поделиться

7 ответов

Скорее создайте свойство для члена префикса - таким образом вы можете установить для свойства значение виртуальное / абстрактное

Поля используются для хранения состояния объекта, они помогают объекту инкапсулировать данные и скрывать проблемы реализации от других. Имея возможность переопределить поле, мы передаем проблемы реализации класса клиентскому коду (включая подтипы). Из-за этого большинство языков приняли решение, что нельзя определять переменные экземпляра, которые можно переопределить (хотя они могут быть общедоступными / защищенными ... чтобы вы могли получить к ним доступ).

Вы также не можете помещать переменные экземпляра в интерфейс

3
ответ дан 2 December 2019 в 00:17
поделиться

Статический или не виртуальный метод или свойство - это просто адрес памяти (для упрощения). Виртуальный метод или свойство идентифицируется записью в таблице. Эта таблица зависит от класса, определяющего метод или свойство. Когда вы переопределяете виртуальный член в производном классе, вы фактически изменяете запись в таблице для производного класса, чтобы указать на метод переопределения. {{ 1}} Во время выполнения доступ к такому члену всегда осуществляется через таблицу. Таким образом, запись может быть переопределена любым производным классом.

Для полей такого механизма нет, поскольку к ним можно быстро получить доступ.

Использование слова «новый» для члена означает, что вы не хотите переопределять запись в таблице, но хотите создать новый член (с тем же именем, что и у существующего виртуального, плохая практика, если вы спросите меня) .

Если вы обращаетесь к виртуальному члену через указатель на базовый класс, вы никогда не получите доступ к члену, определенному как «новый» в производном классе, в чем разница, упомянутая во второй части вашего вопроса.

3
ответ дан 2 December 2019 в 00:17
поделиться

Переопределение поля на самом деле не имеет смысла. Оно является частью состояния базового класса, и если наследующий класс хочет его изменить, оно должно быть изменяемым в наследующем классе путем придания ему соответствующей видимости.

В вашем случае можно было бы установить prefix в конструкторе наследуемого класса:

// Base class field declaration and constructor
protected string prefix;

public Transaction()
{
  prefix = "Primary";
}

// Child class constructor
public SecondaryTransaction()
{
  prefix = "Secondary";
}

Вы также можете сделать свойство вместо поля и сделать его виртуальным. Это позволит вам изменить поведение getter и setter для свойства в наследующем классе:

// Base class
public virtual string Prefix { get { /* ... */ } set { /* ... */ } }

// Child class
public override string Prefix { get { /* ... */ } set { /* ... */ } }

EDIT: Что касается вашего вопроса об использовании переменной в базовом конструкторе до того, как наследующий класс установит ее, один из способов решить это - определить метод инициализации в базовом классе, переопределить его в наследующем классе и вызвать его из базового конструктора до доступа к любым полям:

// Base class
public class Base
{
  protected string prefix;

  public Base()
  {
    Initialize();
    Console.WriteLine(prefix);
  }  

  protected virtual void Initialize()
  {
    prefix = "Primary";
  }
}

// Inheriting class
public class Child : Base
{
  public override void Initialize()
  {
    prefix = "Secondary";
  }
}

EDIT 2: Вы также спросили, в чем разница между virtual/override и name hiding (новое ключевое слово в методах), следует ли их избегать, и могут ли они быть полезны.

Сокрытие имен - это функция, которая нарушает наследование в случае сокрытия виртуальных методов. То есть, если скрыть метод Initialize() в дочернем классе, то базовый класс его не увидит и не вызовет. Также, если метод Initialize() был публичным, внешний код, вызывающий Initialize() по ссылке базового типа, будет вызывать Initialize() базового типа.

Сокрытие имен полезно, когда метод в базовом классе невиртуальный, а дочерний класс хочет предоставить другую реализацию. Обратите внимание, однако, что это НЕ то же самое, что virtual/override. Ссылки на базовый тип будут вызывать реализацию базового типа, а ссылки на дочерний тип будут вызывать реализацию дочернего типа.

8
ответ дан 2 December 2019 в 00:17
поделиться

Вы не можете, потому что в этом нет смысла. Чего бы вы достигли, переопределив поле?

0
ответ дан 2 December 2019 в 00:17
поделиться

В вашем примере, если вы не переопределяете «Show» в классе SecondaryTransaction, то вызов Show в экземпляре SecondaryTransaction фактически будет вызывать метод в базовом классе (Transaction), который, следовательно, будет использовать «Show» в базовом классе, что приведет к выводу:

 Первичный: имя1, состояние1 
Первичный: имя1, состояние1 
 

Итак, в зависимости от того, какой метод вы вызывали (т. е. один из базовый класс или дочерний класс), код будет иметь другое значение для «префикса», что было бы кошмаром ремонтопригодности . Я подозреваю, что вы, вероятно, хотите / должны сделать, это выставить свойство в транзакции, которое обертывает «префикс».

Вы не можете переопределить поле, потому что это деталь реализации базового класса. Вы можете изменить значение защищенного поля, но, переопределив его, вы, по сути, скажете, что я хочу заменить поле , а не значение .

Что бы я сделал (если бы я абсолютно не хотел / не мог использовать свойства):

public class Transaction
{
    public string Name { get; set; }
    public string State { get; set; }
    protected string prefix = "Primary";
    public virtual string Show()
    {
        return String.Format("{0}: {1}, {2}", prefix, Name, State);
    }
}
public class SecondaryTransaction : Transaction
{ 
    public SecondaryTransaction()
    {
        prefix = "Secondary";
    }
    public override string Show()
    {
        return String.Format("{0}: {1}, {2}", prefix, Name, State);
    }
}

Изменить: (Согласно моему комментарию к другому ответу)

Если вы ' повторно вызывается в ctor вашего базового класса и вам нужно установить значение, тогда вам, вероятно, придется изменить транзакцию, возможно, так:

public class Transaction
{
    public string Name { get; set; }
    public string State { get; set; }
    protected string prefix = "Primary";
    // Declared as virtual ratther than abstract to avoid having to implement "TransactionBase"
    protected virtual void Initialise()
    { }
    public Transaction()
    {
        Initialise();
    }
    public virtual string Show()
    {
        return String.Format("{0}: {1}, {2}", prefix, Name, State);
    }
}
public class SecondaryTransaction : Transaction
{ 
    protected override void Initialise()
    {
        prefix = "Secondary";
    }
    public override string Show()
    {
        return String.Format("{0}: {1}, {2}", prefix, Name, State);
    }
}
1
ответ дан 2 December 2019 в 00:17
поделиться

Почему вы хотите переопределить защищенную переменную, разумеется, все, что вы хотите сделать, это установить для нее что-то еще в переопределяющем классе (возможно, в конструкторе) ?

0
ответ дан 2 December 2019 в 00:17
поделиться

Переопределение поля - это нонсенс. Пометив поле как защищенное, вы автоматически получаете доступ к нему в производных классах. Вы можете переопределять функции, свойства, потому что они используют функции внутри класса.

0
ответ дан 2 December 2019 в 00:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: