Прежде всего, ответ на ваш вопрос - нет, C # не поддерживает какую-либо форму ковариации возвращаемого типа для виртуальных переопределений.
Некоторые авторы и комментаторы сказали, что "в этом вопросе нет ковариации". Это неверно; оригинальный постер был совершенно корректен, чтобы поставить вопрос, как они.
Напомним, что ковариантное отображение - это отображение, которое сохраняет существование и направление некоторого другого отношения . Например, отображение типа T
на тип IEnumerable<T>
является ковариантным, поскольку оно сохраняет отношение совместимости присваивания. Если Tiger совместим с присвоением с Animal, преобразование под картой также сохраняется: IEnumerable<Tiger>
совместимо с присвоением с IEnumerable<Animal>
.
Ковариантное отображение здесь немного сложнее увидеть, но оно все еще там. По сути, вопрос заключается в следующем: это должно быть законно?
class B
{
public virtual Animal M() {...}
}
class D : B
{
public override Tiger M() {...}
}
Tiger совместим с назначением с Animal. Теперь сделайте отображение из типа T в метод "public T M ()". Сохраняет ли это сопоставление совместимость ? То есть , если Tiger совместим с Animal для целей присвоения, то public Tiger M()
совместим с public Animal M()
для целей виртуального переопределения?
Ответ на C # - «нет». C # не поддерживает этот тип ковариации.
Теперь, когда мы установили, что вопрос задавался с использованием правильного жаргона алгебры типов, еще несколько мыслей по фактическому вопросу. Первая очевидная проблема заключается в том, что свойство даже не было объявлено как виртуальное, поэтому вопросы виртуальной совместимости спорны. Очевидная вторая проблема заключается в том, что "get; set;" свойство не может быть ковариантным, даже если C # действительно поддерживает ковариацию возвращаемого типа, потому что тип свойства с установщиком - это не просто его тип возвращаемого значения, это также тип его формального параметра . Вам нужно контрастность для формальных типов параметров для обеспечения безопасности типов. Если бы мы позволили возвращать ковариацию типа для свойств с установщиками, то у вас было бы:
class B
{
public virtual Animal Animal{ get; set;}
}
class D : B
{
public override Tiger Animal { ... }
}
B b = new D();
b.Animal = new Giraffe();
и эй, мы только что передали жирафа сеттеру, который ожидает тигра. Если бы мы поддерживали эту функцию, нам пришлось бы ограничить ее возвращаемыми типами (как мы делаем с ковариацией совместимости присваивания на универсальных интерфейсах.)
Третья проблема заключается в том, что CLR не поддерживает этот вид дисперсии; если бы мы хотели поддержать его на языке (как я полагаю, управляемый C ++), то нам пришлось бы предпринять некоторые разумные меры, чтобы обойти ограничения на соответствие сигнатур в CLR.
Вы можете самостоятельно выполнять эти героические меры, тщательно определяя «новые» методы, которые имеют соответствующие типы возвращаемых значений, которые скрывают их типы базовых классов:
abstract class B
{
protected abstract Animal ProtectedM();
public Animal Animal { get { return this.ProtectedM(); } }
}
class D : B
{
protected override Animal ProtectedM() { return new Tiger(); }
public new Tiger Animal { get { return (Tiger)this.ProtectedM(); } }
}
Теперь, если у вас есть экземпляр D, вы видите свойство Tiger-typed. Если вы приведете его к B, вы увидите свойство Animal-typed. В любом случае вы все равно получаете виртуальное поведение через защищенный член.
Короче говоря, у нас нет планов использовать эту функцию, извините.