I have a subclass with an over-ridden method that I know always returns a particular subtype of the return type declared in the base class. If I write the code this way, it won't compile. Since that probably doesn't make sense, let me give a code example:
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();
}
}
Is there any way to accomplish this in C#? If not, what's the best way to achieve something similar? And why isn't it allowed? It doesn't seem to allow any logical inconsistency, since any object returned from the over-ridden method still is BaseReturnType
. Maybe there is something I hadn't considered though. Or maybe the reason is technological or historical.
From Java 5 onwards, we can override a method by changing its return type only by abiding the condition that return type is a subclass of that of overridden method return type.
Rules regarding covariant return type A subclass' overriding method should match its superclass or subclass' return type if the overriding method returns the same type. Underlining methods in subclasses should not override methods in the parent class and vice versa.
You cannot change the return type of method during overriding.
4) What happens if we change the arguments of overriding method? If we change the arguments of overriding method, then that method will be treated as overloaded not overridden.
Unfortunately no, covariant return types aren't supported in C# for method overriding. (Ditto contravariant parameter types.)
If you're implementing an interface you can implement it explicitly with the "weak" version and also provide a public version with the stronger contract. For simple overriding of a parent class, you don't have this luxury I'm afraid :(
(EDIT: Marc has a reasonable solution - although it's pretty ugly, and method hiding is generally a bad thing for readability. No offence meant, Marc ;)
I believe this is actually a CLR restriction, not just a language one - but I could well be wrong.
(As a matter of history, Java (the language) had the same restriction until 1.5 - but it gained covariance at the same time as generics.)
You could make the class generic if that doesn't bothers you:
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();
}
}
You can do this if you introduce an extra method to override (since you can't override
and new
a method with the same name in the same type):
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(); }
}
Now you have a PolymorphicMethod
method at each level with the correct type.
Generics are not necessarily the way to go. In particular, type(of Derived) is not considered a type(of Base).
First, add a new method to your derived class which will return the value with the correct type. Second, mark the overriding method not-overridable and have it delegate to your new method.
That's it. You've solved your problem. Child classes won't be able to re-expand the type because they must override your new method.
I apologize if the code isn't quite right; I'm used to 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};
}
}
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();
}
}
this should work
Change your method signature on Derived class to:
public override BaseReturnType PolymorphicMethod()
{
return new DerivedReturnType();
}
C# doesn't support variant return types. You can check out this post for a way to do this using Generics...http://srtsolutions.com/blogs/billwagner/archive/2005/06/17/covaraint-return-types-in-c.aspx
Here's a sample using Generics in your model:
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With