Почему C # не поддерживает явно реализованные виртуальные методы? - PullRequest
3 голосов
/ 03 июля 2011

Методы интерфейса в C # могут быть реализованы явно, так что их реализация вызывается, когда экземпляр явно приводится к типу интерфейса.Почему это также не поддерживается в виртуальных методах классов?

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

Редактировать: По запросу пример:

public class Foo {
    ...
}

public class Bar : Foo {
   ...
}

class Base {
   abstract Foo A ();
}

class Dervied {
  private Bar _b;

  Bar A () {
    return _b;
  }

  Foo Base.A () {
    return _b;
  }
}

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

Ответы [ 2 ]

5 голосов
/ 03 июля 2011

Некоторые люди рекомендуют вообще не использовать public virtual методы. Но вместо этого создайте один public не виртуальный метод, представляющий интерфейс потребителя, и один protected virtual метод, представляющий интерфейс реализатора.

Я бы не назвал разделение контрактов для звонящего и исполнителя "мутным в дизайне". Во многих случаях это чище ИМО, но я обычно слишком ленив, чтобы действительно так поступить.

Этот дизайн работает намного лучше с ковариацией возвращаемого типа и сокрытием метода.

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

Пример того, как я буду эмулировать ковариацию возвращаемого типа:

public class Base
{
    protected virtual Base FooOverride(int i){return new Base();};//FooOverride does not need to duplicate the argument checking

    public Base Foo(int i)
    {
        if(i<0)
          throw new ArgumentException("i<0");
        return FooOverride(i);
    }
}

public class Derived:Base
{
    protected override Base FooOverride(int i){return new Derived();};
    public new Derived Foo(int i)
    {
        return (Derived)base.Foo();
    }
}
3 голосов
/ 03 июля 2011

Какая польза от этого, если не допустить что-то подобное?

class Base
{
    virtual void M() { }
}

class Derived : Base
{
    override void M() { }

    override void Base.M() { }
}

Это фактически вызывает нарушение принципа подстановки Лискова в язык C # - если у меня есть переменная типа Base, вызов M () для него может делать совершенно разные вещи в зависимости от того, выполняется ли тип времени - Базовый или Производный.

Явная реализация интерфейса отличается. Скажем, у вас есть это:

interface IFoo
{
    void DoStuff();   
}

interface IBar
{
    void DoStuff();
}

class C : IFoo, IBar
{
    void IFoo.DoStuff() { }

    void IBar.DoStuff() { }
}

Это сохраняет LSP - если у меня есть переменная IFoo типа C времени выполнения, вызов DoStuff () для нее получит ее реализацию IFoo. Аналогично с IBar.

...