полиморфизм для свойств, указанных интерфейсами - PullRequest
3 голосов
/ 08 сентября 2010

Почему это не работает?

 public class ClassOptions {}

 public interface Inode {
    ClassOptions Options {get;}
 }            

public class MyClass : Inode {
  public ClassOptions Options { get; set; }
}           

public class ClassDerivedOptions : ClassOptions {
}

public class MyDerivedClass : Inode {
    public ClassDerivedOptions Options { get; set; } << does not implement INode...
}

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

Ответы [ 4 ]

8 голосов
/ 08 сентября 2010

Это не работает, потому что интерфейс INode явно вызывает свойство Options типа ClassOptions.C # не поддерживает ковариацию возвращаемого типа (это то, что вы запрашиваете в данном случае).

Для чего бы то ни было, в Microsoft Connect также есть запрос языковой функции специально для ковариации возвращаемого типа:

Нужны ковариантные типы возврата в C # / всех языках .NET

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

public class MyDerivedClass : INode
{
    public ClassDerivedOptions Options { get; set; }
    public ClassOptions INode.Options { get { return Options; } }
}
4 голосов
/ 08 сентября 2010

Как отмечает Джастин, нужная вам функция называется «ковариация возвращаемого типа» и не поддерживается в C # или, в этом отношении, в системе типов CLR.

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

Не поймите меня неправильно;Я вижу, как это удобно, и я использовал эту функцию в C ++.Но каждый раз, когда он появлялся в программе на C #, я обнаруживал, что могу довольно легко обойти его отсутствие.

(*) Конечно, пять лет назад я бы сказал то же самое о именованных и необязательных параметрах, и теперь они в C # 4. Это возможно для маловероятной возможностидолжно быть реализовано, но спрос должен быть чертовски высоким.

3 голосов
/ 08 сентября 2010

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

public class ClassOptions 
{ }

public class ClassDerivedOptions : ClassOptions 
{ }

public interface INode<T> where T : ClassOptions
{
    T Options { get; }
}            

public class MyClass : INode<ClassOptions> 
{
    public ClassOptions Options { get; set; }
}           

public class MyDerivedClass : INode<ClassDerivedOptions>
{
    public ClassDerivedOptions Options { get; set; }
}
1 голос
/ 08 сентября 2010

Стандартный способ справиться с этой ситуацией - реализовать интерфейс явно:

public class MyDerivedClass : Inode {

    // New, more specific version:
    public ClassDerivedOptions Options { get; set; }

    // Explicit implementation of old, less specific version:
    ClassOptions Inode.Options
    {
        get { return Options; }
    }
}

Вот как большинство старых реализаций IList работали до обобщения, например: указание более специфического свойства T this[int index] и затем явная реализация object IList.this[int index], вызывающая исключение, когда set вызывается с объектом объекта неправильный тип.

В приведенном вами примере кода вам даже не требуется явный set, поскольку он не является членом вашего интерфейса Inode.

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