tags:

views:

1482

answers:

8

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.

+4  A: 

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.)

Jon Skeet
None taken ;-p Note, however, that this is exactly the pattern used by a lot of the .NET BCL - for example DbConnection has CreateCommand, which shims thru the "protected abstract" CreateDbCommand.
Marc Gravell
See this comment by Eric Lippert about why C# doesn't support it http://stackoverflow.com/questions/1319663/why-c-doesnt-allow-inheritance-of-return-type-when-implementing-an-interface/1320710#1320710
Ben Lings
A: 

It seems to me that you need to be returning an interface, not a base class.

Greg Hurlman
Firstly that's not always applicable, and secondly it may not help anyway - you might want to indicate that you're returning an implementation of ISpecificInterface where the base class declares that it will return IGeneralInterface.
Jon Skeet
+1  A: 
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

anand
I think the OP wants the method to be *declared* as DerivedReturnType, not just to return one.
Marc Gravell
I know this is possible, but then it requires you to cast (new DerivedClass()).PolymorphicMethod() if you want to use it as a DerivedReturnType. That may be what I end up doing, but I don't like casting if I can avoid it.
recursive
he can cast the returnval wherever its needed as Derived
anand
damn I type too slow :P
anand
@anand: Yes, but that's painful, as well as making a check necessary at execution time when we should be able to explain that it's valid at compile time.
Jon Skeet
+3  A: 

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.

Marc Gravell
+9  A: 

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();
        }
    }
Stormenet
+1  A: 

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();
    }
}
JoshBerke
A: 

Heh, I was all about to post my answer, but Stormenet and Josh beat me to it.

The answer is, use Generics.

ZaijiaN
+1  A: 

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};
    }
}
Strilanc