2009-01-07 2 views
23

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

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. Возможно, я кое-что не рассмотрел. Или, может быть, причина технологическая или историческая.

+0

Что говорит комплимент? –

+0

Я включил ошибку компилятора в качестве комментария. – recursive

ответ

17

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

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

(EDIT: Marc имеет reasonable solution - хотя это довольно уродливо, а метод скрытия - это, как правило, плохо для чтения. преступление означало, Марк;).

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

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

+1

Ничего не взято; -p Обратите внимание, однако, что это точно шаблон, используемый многими .NET BCL - например, DbConnection имеет CreateCommand, который перемещается через «защищенный абстрактный» CreateDbCommand. –

+1

Смотрите этот комментарий Эрика Липперта о том, почему C# не поддерживает его http://stackoverflow.com/questions/1319663/why-c-doesnt-allow-inheritance-of-return-type-when-implementing-an-interface/1320710 # 1320710 –

0

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

+1

Во-первых, это не всегда применимо, а во-вторых, это может не помочь - вы можете указать, что вы возвращаете реализацию ISpecificInterface, где базовый класс объявляет, что он вернет IGeneralInterface. –

1
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

Я думаю, что OP хочет, чтобы метод был объявлен * как DerivedReturnType, а не только для его возвращения. –

+0

Я знаю, что это возможно, но тогда это требует от вас бросить (новый DerivedClass()). PolymorphicMethod(), если вы хотите использовать его как DerivedReturnType. Возможно, это то, что я делаю, но мне не нравится кастинг, могу ли я избежать этого. – recursive

+0

он может отбрасывать returnval везде, где это необходимо, поскольку Derived – anand

12

Вы можете сделать это, если ввести дополнительный метод, чтобы переопределить (так как вы не можете 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 на каждом уровне с правильным типом.

16

Вы могли бы сделать класс общим, если это не мешает вам:

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(); 
     } 
    } 
1

Измените свой метод подписи на производный класс:

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

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

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

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(); 
    } 
} 
2

Дженерики не обязательно путь. В частности, тип (из 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}; 
    } 
} 
Смежные вопросы