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

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

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()         |

Надеюсь, таблица вам понятна. У меня два вопроса:

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

person Lukasz Lysik    schedule 23.03.2010    source источник
comment
Не могли бы вы уточнить, что вы хочете, а не то, что вы испытали? Все, что я могу сказать прямо сейчас, это то, что да, это выглядит правильно. Что касается последнего вопроса, вам действительно следует прочитать о том, как работает диспетчеризация виртуальных методов. Для получения информации см. en.wikipedia.org/wiki/Virtual_method_table.   -  person Lasse V. Karlsen    schedule 23.03.2010


Ответы (4)


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

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

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

person SLaks    schedule 23.03.2010


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

person Nick Larsen    schedule 23.03.2010

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

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

person Seb    schedule 23.03.2010