tags:

views:

333

answers:

10

Given the following classes:

ClassA
{
     public ClassA DoSomethingAndReturnNewObject()
     {}    
}

ClassB : ClassA
{}

ClassC : ClassA
{}

Is there a way to get ClassB and ClassC to inherit the method but customize the return type to their own class?

I prefer not to copy the method from ClassA and change the type there.

I need to get a ClassB object when I call ClassB.DoSomethingAndReturnNewObject().

I need to get a ClassC object when I call ClassC.DoSomethingAndReturnNewObject().

Something like calling a constructor based on current type like: this.GetType()? But I have no clue how to actually do that.

+7  A: 

No, the feature you mentioned is called return type covariance. It's not supported in C#.

Mehrdad Afshari
although i think it will be in c# 4.0 no?
jk
jk: No. I don't think C# is going to support this feature. C# 4.0 supports *safe co-/contra-variance for generics*.
Mehrdad Afshari
Googling that term also yields this suggestion on Microsoft Connect: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=90909
Wim Coenen
Correct. We are adding generic variance, not return type covariance.
Eric Lippert
+3  A: 

What you're describing is a covariant return type and is not supported in C#.

However, you could create ClassA as an open generic and have the closed generic inheritors return their own type.

Example:

public abstract class ClassA<T> where T: ClassA<T>, new()
{
    public abstract T DoSomethingAndReturnNewObject();
}

public class ClassB: ClassA<ClassB>
{
    public override ClassB DoSomethingAndReturnNewObject()
    {
        //do whatever
    }
}
Joseph
"ClassB and ClassC can't inherit from ClassA and redefine the return type while still supporting polymorphism" - I'm not sure what you mean by that; if it's a generic statement, then it's false. What he's asking for is a covariant return type. This is supported by both C++ and Java, and quite obviously doesn't "break polymorphism" - it's perfectly typesafe, if that's what's implied. It's just a limitation of CLR.
Pavel Minaev
You would really need `new` in the Generic constraint and the Class would have to support a default constructor. I'm guessing this from the name DoSomethingAndReturn__NewObject__()
AnthonyWJones
@Anthony Oh good catch, I wasn't really paying attention to the name of the method. I was thinking the OP would be modifying the current instance and just return this;
Joseph
@Pavel it works as long as it follows the Liskov substitution principle, that's true. I'll modify my answer to reflect.
Joseph
A: 

I think that you need to create a new function with the new return type and call inside the other function, and manipulate the type returned. otherwise it will no be logical ! (with the definition/idea of inheritance)

Dani
If downvote- please explain...
Dani
A: 

Maybe generics will be your saviour. Take a look at this link: C# Generics Part 3/4: Casting, Inheritance, and Generic Methods

bstract class  B<T> {
   public abstract T Fct(T t);
}
class D1 : B<string>{
   public override string Fct( string t ) { return "hello"; }
}
class D2<T> : B<T>{
   public override T Fct(T t) { return default (T); }
}
Philip Wallace
A: 

Well, the correct answer is no and, generally, this is a bad idea. If you are returning something completely different, find another way.

However, if you aren't returning something completely different, an interface can solve your issue. Instead of returning a class, return an interface and have classes A, B, and C return objects that implement that interface in the way they see fit.

Ken Wootton
The return type is not "something completely different". There is a "is-a" relationship between ClassB and ClassA. Declaring a more specific return type of the original return value does not violate the Liskov Substitution Principle; in fact, java allows it since java 1.5. For input arguments the opposite is true: declaring more generic input arguments should be allowed in derived classes.
Wim Coenen
Yes but the ultimate question is why does one need to care about it being class B or class C? IF you need something specific then his point is correct.
Min
wcoenen, I'm not sure I understand your issue. The current popular answer is a fine solution. If you are saying that the subclass should actually have a more specific return value (which isn't supported, so I don't know why we are discussing this), then I disagree. It's bad form.
Ken Wootton
A: 

This isn't inheritance, because the return type of the method is part of its signature. You're not changing the method, you'd be creating an entirely new one.

You do have some options. You could, for example, make the method DoSomethingAndReturnNewObject a generic-based method, and have it return its generic type. This is probably the most direct path to the exact behavior for which you're looking.

The other alternative is to leave the method signatures as-is, and have the subclass methods return instances of ClassB and ClassC. Then the client code would need to be responsible for casting in order to use those objects as their appropriate derived classes.

Is there some reason the common interface of ClassA doesn't suffice? Polymorphism will, if you have derived and overridden virtual members correctly, provide you with the correct functionality if you're only using the ClassA members.

John Rudy
+3  A: 

You need to create a protected virtual method for the DoSomethingAndReturnNewObject to use:

class ClassA
{
    protected virtual ClassA Create()
    {
        return new ClassA()
    }

    public ClassA DoSomethingAndReturnNewObject()
    {
        ClassA result = Create();
        // Do stuff to result
        return result;
    }
}

class ClassB : ClassA
{
     protected override ClassA Create() { return new ClassB(); }
}

class ClassC : ClassA
{
     protected override ClassA Create() { return new ClassC(); }
}

Note the return type remains ClassA but the object instance type will be the specific class.

AnthonyWJones
Good thinking, I like this one.
Peterdk
+1  A: 
class ClassA<T> where T : ClassA<T>, new()
{
    public T DoSomethingAndReturnNewObject()
    {
        return new T();
    }
}

class ClassB : ClassA<ClassB> { }

class ClassC : ClassA<ClassC> { }

Test:

ClassB b1 = new ClassB();

ClassB b2 = b1.DoSomethingAndReturnNewObject(); // returns instance of ClassB
dtb
A: 

I finally devised the following solution. For my case it's useful enough, but it assumes that ClassA knows about it's derivatives and is limited to those two options. Not really high-level thinking, but it works. :)

ClassA
{
    public ClassA DoSomethingAndReturnNewObject()
    {
        if (this.GetType() == typeOf(ClassB))
        {
            return new ClassB(values);
        }
        else
        {
            return new ClassC(values):
        }
    }    
}

ClassB : ClassA
{}

ClassC : ClassA
{}
Peterdk
A: 

You have a snippet of common code that you want Class B and Class C to call, that also returns their type? Yes you can do this.

This is what you are aiming for right?


ClassX
{
     public ClassX DoACommonOperationButReturnWhateverClass()
     {
        //eg. applySameTransformationToWhateverClass'sCoordinatesAndReturnNewResizedObject
     }
}

ClassB
{
    //declare the common operation which would return ClassB, without writing more code

}

ClassC
{
    //declare the common operation which would return ClassA, without writing more code
}

Make ClassX into a template, and don't even bother Inheriting, just use Composition:


ClassX<T>
{
    public T makeThisThatters (){
       T retObj = new T();
       retObj.dothis();
       retObj.dothat();   //functions that are common to ClassB and ClassC
       return retObj;
    }
}

ClassB
{
  public ClassX<ClassB> factory;
}

ClassC
{
  public ClassX <ClassC> factory;
}

ClassB cb = new ClassB
ClassB cb2 = cb.factory.makeThisThatters();


ClassC cc = new ClassC
ClassC cc2 = cc.factory.makeThisThatters();

(Caveat - the syntax might be wrong, I don't use C#, but hopefully you get the gist)

Riaz Rizvi