С# Скрытие, переопределение и вызов функции из базового класса

Я изучаю С#, и я столкнулся с следующей проблемой. У меня есть два класса: base и производные:

class MyBase
{
    public void MyMethod()
    {
        Console.WriteLine("MyBase::MyMethod()");
    }
}


class MyDerived: MyBase
{
    public void MyMethod()
    {
        Console.WriteLine("MyDerived::MyMethod()");
    }
}

Пока, без virtual и override ключевых слов. Когда я скомпилирую это, я получаю предупреждение (которое, конечно же, ожидается), что я пытаюсь скрыть MyMethod от класса MyBase.

Что я хочу сделать, так это вызов метода из базового класса, имеющего экземпляр производного класса. Я делаю это вот так:

MyDerived myDerived = new MyDerived();
((MyBase)myDerived).MyMethod();

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

| MyBase::MyMethod | MyDerived::MyMethod | Result printed on the console |
| -----------------|---------------------|-------------------------------|
| -                | -                   | MyBase::MyMethod()            |
| -                | new                 | MyBase::MyMethod()            |
| virtual          | new                 | MyBase::MyMethod()            |
| virtual          | override            | MyDerived::MyMethod()         |

Я надеюсь, что вам будет ясно. У меня есть два вопроса:

  • Правильно ли это вызов функции из базового класса (((MyBase)myDerived).MyMethod();)? Я знаю о base ключевое слово, но его можно вызвать только изнутри производного класса. Правильно ли?
  • Почему в последнем случае (с модификаторами virtual и override) метод, который был вызван, пришел из производного класса? Не могли бы вы объяснить это?

Ответы

Ответ 1

Когда вы вызываете метод virtual в экземпляре типа, который переопределяет метод, переопределенная версия всегда будет вызываться, даже если вы отбрасываете базовый класс.

Единственный способ вызвать базовую реализацию виртуального метода для класса, который переопределяет этот метод, заключается в том, чтобы сделать второй метод в производном классе (а не в базовом классе), который вызывает метод с использованием ключевого слова base.

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

Ответ 2

Что касается вашего второго вопроса, вы не меняете тип объекта, на который вы ссылаетесь, просто интерфейс, на который вы ссылаетесь. Поэтому, если у вас есть объект B, который наследует от A и переопределяет функцию C, даже если вы ссылаетесь на B как на A, он по-прежнему вызывает реализации самого производного типа, в данном случае B.

Ответ 3

Вы правы - base может быть вызван только из производного класса - Источник.

На этой странице также приведен пример того, как переопределить определение базового класса.

Ответ 4

  • Вы правы, base ссылается на базовый класс для данного экземпляра.
  • Привлеченный механизм называется полиморфизмом: лучше работать без предупреждения. Хороший путь, ориентированный на естественный объект, - это последний из упомянутых вами случаев.

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