Вопрос

Насколько я знаю, в C # 2.0 невозможно выполнить следующее

public class Father
{
    public virtual Father SomePropertyName
    {
        get
        {
            return this;
        }
    }
}

public class Child : Father
{
    public override Child SomePropertyName
    {
        get
        {
            return this;
        }
    }
}

Я решаю проблему, создавая свойство в производном классе как "новое", но, конечно, оно не является полиморфным.

public new Child SomePropertyName

Есть ли какое-нибудь решение в версии 2.0?Как насчет каких-либо функций в 3.5, которые решают этот вопрос?

Это было полезно?

Решение

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

class B {
    S Get();
    Set(S);
}
class D : B {
    T Get();
    Set(T);
}

Для Get методы, ковариация означают, что T должно быть либо S или тип, производный от S.В противном случае, если у вас была ссылка на объект типа D хранится в переменной , введенной B, когда ты позвонил B.Get() вы не получили бы объект, представимый в виде S назад - нарушение системы типов.

Для Set методы, контравариантность означают, что T должно быть либо S или тип , который S происходит из.В противном случае, если у вас была ссылка на объект типа D хранится в переменной , введенной B, когда ты позвонил B.Set(X), где X был в своем роде S но не в своем роде T, D::Set(T) получил бы объект типа, которого он не ожидал.

В C # было сознательное решение запретить изменение типа при перегрузке свойств, даже если у них есть только один из пары getter / setter, потому что в противном случае это имело бы очень непоследовательное поведение ("Ты имеешь в виду, что я могу изменить тип на тот, у которого есть геттер, но не на тот, у которого есть и геттер, и сеттер?Почему бы и нет?!?" -- Анонимный новичок в Альтернативной вселенной).

Другие советы

Вы можете повторно объявить (new), но вы не можете повторно объявить и переопределить одновременно (с тем же именем).Одним из вариантов является использование защищенного метода для скрытия деталей - это допускает как полиморфизм, так и скрытие одновременно:

public class Father
{
    public Father SomePropertyName
    {
        get {
            return SomePropertyImpl();
        }
    }
    protected virtual Father SomePropertyImpl()
    {
        // base-class version
    }
}

public class Child : Father
{
    public new Child SomePropertyName
    {
        get
        { // since we know our local SomePropertyImpl actually returns a Child
            return (Child)SomePropertyImpl();
        }
    }
    protected override Father SomePropertyImpl()
    {
        // do something different, might return a Child
        // but typed as Father for the return
    }
}

Нет, но вы можете использовать дженерики в версии 2 и выше:

public class MyClass<T> where T: Person
{
    public virtual T SomePropertyName
    {
        get
        {
            return  ...;
        }
    }
}

Тогда Отец и потомок являются универсальными версиями одного и того же класса

От Википедия:

В языке программирования C # была добавлена поддержка как возвращаемого типа ковариации, так и параметра контравариантности для делегатов в версии 2.0 языка.Ни ковариация, ни контравариантность не поддерживаются для переопределения метода.

Однако в нем явно ничего не говорится о ковариации свойств.

Вы можете создать общий интерфейс для отца и потомка и возвращать тип этого интерфейса.

Нет.C # не поддерживает эту идею (это называется "ковариация возвращаемого типа").Однако вы можете сделать это:

public class FatherProp
{
}

public class ChildProp: FatherProp
{
}


public class Father
{
    public virtual FatherProp SomePropertyName
    {
        get
        {
            return new FatherProp();
        }
    }
}


public class Child : Father
{
    public override FatherProp SomePropertyName
    {
        get
        {
            // override to return a derived type instead
            return new ChildProp();
        }
    }
}

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

Возможно (но неаккуратно) протестировать возвращаемый объект на предмет его фактического типа (т. е."if SomeObject is ChildProp"), но лучше вызвать для него виртуальный метод, который выполняет правильные действия для своего типа.

Виртуальный метод базового класса (в данном случае virtual property) не только имеет реализацию, но и определяет контракт:что дочерний класс может предоставить другую реализацию SomePropertyName, если он соответствует этому контракту (т. е.SomePropertyName возвращает объект типа "FatherProp").Возврат объекта типа "ChildProp", производного от "FatherProp", соответствует этому контракту.Но вы не можете изменить контракт в разделе "Дочерний" - этот контракт применяется ко всем классам, происходящим от "Отца".

Если вы сделаете шаг назад и посмотрите на свой дизайн в более широком плане, в C # toolkit есть другие языковые конструкции, о которых вы, возможно, также захотите подумать - дженерики или интерфейсы.

Нет.C # не поддерживает эту идею (это называется "возвращаемый тип ковариация").

Из Википедии:

В языке программирования C # добавлена поддержка как возвращаемого типа ковариации, так и параметра контравариантность для делегатов в версии 2.0 языка.Ни ковариация, ни контравариантность не поддерживаются для переопределения метода.

Вы можете повторно объявить (new), но вы не можете повторно объявить и переопределить одновременно (с тем же именем).Один вариант-использовать защищенный метод скрыть детали - это позволяет и полиморфизм и скрывая в то же время:

Лучшим решением было бы использовать дженерики:

public class MyClass<T> where T: Person
{
   public virtual T SomePropertyNameA
   {        
      get { return  ...; }    
   }
}//Then the Father and Child are generic versions of the same class

Это самое близкое, к чему я мог подойти (пока).:

    public sealed class JustFather : Father<JustFather> {}

    public class Father<T> where T : Father<T>
    { public virtual T SomePropertyName
        { get { return (T) this; }
        }
    }

    public class Child : Father<Child>
    { public override Child SomePropertyName
        { get { return  this; }
        }
    }

Без JustFather класс, вы не смогли бы создать экземпляр Father<T> если только это не был какой-то другой производный тип.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top