Неожиданное поведение наследования и интерфейсов в c # - PullRequest
2 голосов
/ 03 марта 2009

Сегодня, реализуя некоторые тестовые классы в c #, я наткнулся на некоторые вопросы, касающиеся наследования (и интерфейсов) в c #. Ниже приведен пример кода, иллюстрирующий мои вопросы.

interface ILuftfahrzeug
{
    void Starten();
}

class Flugzeug : ILuftfahrzeug
{
    public void Starten()
    {
        Console.WriteLine("Das Flugzeug startet, "+Dings());
    }

    protected string Dings()
    {
        return "Flugzeug Dings";
    }
}


class Motorflugzeug : Flugzeug, ILuftfahrzeug
{
    public new void Starten()
    {
        Console.WriteLine("Das Motorflugzeug startet, "+Dings());
    }

    protected new string Dings()
    {
        return "Motorflugzeug Dings";
    }
}

class InterfaceUndVererbung
{
    static void Main(string[] args)
    {
        //Motorflugzeug flg = new Motorflugzeug(); // case1: returns "Das Motorflugzeug startet, Motorflugzeug Dings"
        //Flugzeug flg = new Motorflugzeug(); // case2: returns "Das Flugzeug startet, Flugzeug Dings"
        ILuftfahrzeug flg = new Motorflugzeug(); // case3: returns "Das Motorflugzeug startet, Motorflugzeug Dings"
                    // if Motorflugzeug implements ILuftfahrzeug explicitly, 
                    // otherwise "Das Motorflugzeug startet, Motorflugzeug Dings"

        flg.Starten();
        Console.ReadLine();
    }
}

Вот мои вопросы:

  1. Объявление и инициализация с помощью Flugzeug flg = new Motorflugzeug (); (case2) Я ожидал, что вместо Flugzeug.Starten вызывается Motorflugzeug.Starten (и я почти уверен, что это поведение, которое демонстрирует Java). openbook.galileo говорит, что в этом случае с использованием c # тип времени выполнения - Flugzeug. Есть ли причина для этого? Для меня такое наследственное поведение не имеет смысла.
  2. То же самое с ILuftfahrzeug flg = new Motorflugzeug (); (case3) - здесь я мог бы обойтись, позволив Motorflugzeug реализовать ILuftfahrzeug явно (как в коде примера). Но для меня это избыточно, поскольку Flugzeug уже реализует ILuftfahrzeug.
  3. Теперь я хочу перезаписать защищенные методы Dings (), которые вызываются из Starten (). Если я запускаю код, как он реализован в примере, все работает нормально. Но если Starten () не реализован в Motorflugzeug, вместо Motorflugzeug.Dings () будет вызываться Dings () базового класса. Мне сказали, что Java также показывает это поведение.
    Есть ли способ обойти это? В противном случае мне пришлось бы перезаписать каждый метод (здесь: Starten ()), который вызывает метод, который я на самом деле намереваюсь перезаписать (здесь: Dings ()), даже если он точно такой же, как в базовом классе.

Ответы [ 7 ]

9 голосов
/ 03 марта 2009

1: вы повторно объявляете метод (new); если вы override это должно работать. new нарушает любое полиморфное поведение.

2: вы повторно реализуете интерфейс; это действительно вызывает высочайшую реализацию. Опять же, override исправит это.

class Flugzeug : ILuftfahrzeug {
    public virtual void Starten() {
        Console.WriteLine("Das Flugzeug startet, " + Dings());
    }    
    protected virtual string Dings() {
        return "Flugzeug Dings";
    }
}
class Motorflugzeug : Flugzeug {
    public override void Starten() {
        Console.WriteLine("Das Motorflugzeug startet, " + Dings());
    }    
    protected override string Dings() {
        return "Motorflugzeug Dings";
    }
}
1 голос
/ 03 марта 2009

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

Обновление 1: Новое ключевое слово явно указывает на тот факт, что простое объявление не виртуального метода в унаследованном классе просто скрывает базовый метод при работе с этим классом напрямую. Всякий раз, когда вы работаете с базовым классом, нечего скрывать.

0 голосов
/ 03 марта 2009

Я второй пункт Фредди Риос о виртуальном ключевом слове. Если ваш базовый класс не объявит метод с виртуальным ключевым словом, полиморфное поведение вообще не будет. Неважно, используете ли вы переопределение или новый.

0 голосов
/ 03 марта 2009

Причина, по которой ваша программа не делает того, чего вы ожидаете, заключается в ключевом слове new в вашем методе.

c # метод не связан динамически по умолчанию. Это означает, что вызываемый метод определяется во время компиляции. Вы можете посмотреть, как тип переменной определяет, какой метод вызывается вместо фактического объекта в памяти.

Чтобы достичь желаемого эффекта, вам нужно убедиться, что c3 динамически связывает метод. Вы можете сделать это, объявив метод virtual и метод переопределения override.

class Flugzeug : ILuftfahrzeug
{
    public virtual void Starten()
    {
        Console.WriteLine("Das Flugzeug startet, "+Dings());
    }

    protected virtual string Dings()
    {
        return "Flugzeug Dings";
    }
}


class Motorflugzeug : Flugzeug, ILuftfahrzeug
{
    public override void Starten()
    {
        Console.WriteLine("Das Motorflugzeug startet, "+Dings());
    }

    protected override string Dings()
    {
        return "Motorflugzeug Dings";
    }
}

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

0 голосов
/ 03 марта 2009

Реализованные элементы интерфейса не являются автоматически виртуальными. Вы должны сделать их виртуальными, абстрактными и т. Д.

0 голосов
/ 03 марта 2009

Это потому, что вы используете "новый" модификатор, см. MSDN

Вместо этого вы должны использовать "override"

0 голосов
/ 03 марта 2009

new keywork и ключевое слово override делают две совершенно разные вещи, и вы испытываете поведение new, где из вашего описания я думаю, что вы хотите использовать override, так как это следует за обычно ожидаемым поведением наследования. Вам нужно будет объявить метод / свойство virtual в базовом классе и использовать переопределение, а не новый.

Доу, слишком медленно!

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