Как вернуть подтип в переопределенный метод подкласса в C #? - PullRequest
28 голосов
/ 07 января 2009

У меня есть подкласс с переопределенным методом, который, как я знаю, всегда возвращает определенный подтип возвращаемого типа, объявленного в базовом классе. Если я напишу код таким образом, он не скомпилируется. Поскольку это, вероятно, не имеет смысла, позвольте мне привести пример кода:

class BaseReturnType { }
class DerivedReturnType : BaseReturnType { }

abstract class BaseClass {
    public abstract BaseReturnType PolymorphicMethod();
}

class DerivedClass : BaseClass {
    // Compile Error: return type must be 'BaseReturnType' to match 
    // overridden member 'BaseClass.PolymorphicMethod()'
    public override DerivedReturnType PolymorphicMethod() { 
        return new DerivedReturnType(); 
    }
}

Есть ли способ сделать это в C #? Если нет, то как лучше всего добиться чего-то подобного? И почему это не разрешено? Кажется, это не допускает никакой логической несогласованности, поскольку любой объект, возвращенный из переопределенного метода, все еще is BaseReturnType. Может быть, есть то, что я не учел, хотя. Или, может быть, причина технологическая или историческая.

Ответы [ 7 ]

18 голосов
/ 07 января 2009

К сожалению, нет, ковариантные возвращаемые типы не поддерживаются в C # для переопределения метода. (То же тип контравариантных параметров.)

Если вы реализуете интерфейс, вы можете явно реализовать его со «слабой» версией, а также предоставить общедоступную версию с более сильным контрактом. Боюсь, что для простого переопределения родительского класса у вас нет такой роскоши :(

(РЕДАКТИРОВАТЬ: у Марка есть разумное решение - хотя это довольно уродливо, а сокрытие методов, как правило, плохо для удобства чтения. Без обид, Марк,;)

Я считаю это на самом деле ограничение CLR, а не только языковое, но я вполне могу ошибаться.

(С точки зрения истории, Java (язык) имел то же ограничение до 1,5 - но он получил ковариацию в то же время, что и дженерики.)

17 голосов
/ 07 января 2009

Вы можете сделать класс родовым, если вас это не беспокоит:

    class BaseReturnType { }
    class DerivedReturnType : BaseReturnType { }

    abstract class BaseClass<T> where T : BaseReturnType
    {
        public abstract T PolymorphicMethod();
    }

    class DerivedClass : BaseClass<DerivedReturnType>
    {
        // Error: return type must be 'BaseReturnType' to match 
        // overridden member 'BaseClass.PolymorphicMethod()'
        public override DerivedReturnType PolymorphicMethod()
        {
            return new DerivedReturnType();
        }
    }
12 голосов
/ 07 января 2009

Вы можете сделать это, если введете дополнительный метод для переопределения (поскольку вы не можете override и new метод с одинаковым именем в том же типе):

abstract class BaseClass
{
    public BaseReturnType PolymorphicMethod()
    { return PolymorphicMethodCore();}

    protected abstract BaseReturnType PolymorphicMethodCore();
}

class DerivedClass : BaseClass
{
    protected override BaseReturnType PolymorphicMethodCore()
    { return PolymorphicMethod(); }

    public new DerivedReturnType PolymorphicMethod()
    { return new DerivedReturnType(); }
}

Теперь у вас есть PolymorphicMethod метод на каждом уровне с правильным типом.

2 голосов
/ 08 января 2009

Дженерики - не обязательно путь. В частности, тип (из Derived) не считается типом (из Base).

Сначала добавьте новый метод в ваш производный класс, который будет возвращать значение с правильным типом. Во-вторых, отметьте переопределенный метод как не перезаписываемый и попросите его делегировать новый метод.

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

Прошу прощения, если код не совсем правильный; Я привык к VB.net.

abstract class C1 {
    public abstract IEnumerable<Byte> F1();
}
class C2 : C1 {
    public sealed override IEnumerable<Byte> F1() {
        Return F2();
    }
    public overridable IList<Byte> F2() {
        Return {1, 2, 3, 4};
    }
}
1 голос
/ 07 января 2009

Измените сигнатуру вашего метода в производном классе на:

 public override BaseReturnType PolymorphicMethod() 
 {
    return new DerivedReturnType();     
 }

C # не поддерживает варианты возврата. Вы можете проверить этот пост для способа сделать это, используя Generics ... http://srtsolutions.com/blogs/billwagner/archive/2005/06/17/covaraint-return-types-in-c.aspx

Вот пример использования Generics в вашей модели:

public class BaseReturnType
{
}
public class DerivedReturnType : BaseReturnType
{
}

public abstract class BaseClass<T> where T : BaseReturnType
{
    public abstract T PolymorphicMethod();

}

public class DerviedClass : BaseClass<DerivedReturnType>
{
    public override DerivedReturnType PolymorphicMethod()
    {
        throw new NotImplementedException();
    }
}
1 голос
/ 07 января 2009
class BaseReturnType { }
class DerivedReturnType : BaseReturnType { }

abstract class BaseClass {
    public abstract BaseReturnType PolymorphicMethod();
}

class DerivedClass : BaseClass {
    // Error: return type must be 'BaseReturnType' to match 
    // overridden member 'BaseClass.PolymorphicMethod()'
    public override BaseReturnType PolymorphicMethod() { 
        return new DerivedReturnType(); 
    }
}

это должно работать

0 голосов
/ 07 января 2009

Мне кажется, что вам нужно возвращать интерфейс, а не базовый класс.

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