9

I'd like to be able to have a method in a C# base class, callable on objects of several derived classes, that returns the object itself, and have the CLR know what type the object really is - i.e., the appropriate derived type. Can someone suggest a way to do it? Other, of course, than return type covariance, which C# doesn't have.

Something like this, except that Method()'s return type should be the type of the derived class, not the base:

public abstract class Base { 
    public Base Method() { return this; }
}

public class Derived1: Base { ... }

public class Derived2: Base { ... }

public class Main {
    public static int Main() {
        Derived1 d1 = new Derived1();
        Derived1 x = d1.Method();
        Derived2 d2 = new Derived2();
        Derived2 y = d2.Method();
    }
}

I can only think of two ways to make this work, and I don't like either of them:

  1. Cast the result of Method() to the expected type (e.g., Derived1 x = (Derived) d1.Method();). But casts are the tool of the Devil, and besides, the intent of the method is to return a Derived1 or Derived2 or ..., not a Base.

  2. Declare Method() as abstract in the base and implement it separately in each derived class. But that runs exactly counter to the idea of factoring out common methods. The Method() would be identical in each case except for its return type.

svick
  • 236,525
  • 50
  • 385
  • 514
Ross Patterson
  • 9,527
  • 33
  • 48

7 Answers7

11

Okay, I misread the question. I'd thought the OP did want to override the method. Apparently not, so generics are the way forward:

public abstract class Base<T> where T : Base<T>
{
    public T Method()
    {
        return (T) (object) this;
    }
}

public class Derived1 : Base<Derived1>
{
}

There's still a cast, but that's unfortunately unavoidable as far as I'm aware. You'd almost certainly want to check it in the constructor, too:

public Base()
{
    if (this as T == null)
    {
        // throw some exception
    }
}

It's ugly, but it'll work... and the ugliness is confined to the base class.


Original answer

One way to do it would be to put Method into a generic interface and implement it explicitly:

public interface IFoo<T> {
    T Method();
}

public abstract class Base : IFoo<Base>
{
    Base IFoo<Base>.Method()
    {
        return this;
    }
}

public class Derived1 : IFoo<Derived1>
{
    public Derived1 Method()
    {
        // If you need to call the base version, you'll
        // need ((IFoo<Base>)this).Method()
        return this;
    }
}

It's not nice, but it would work... where possible, I think I'd try to avoid needing it, to be honest. (And yes, I've come across similar situations when implementing protocol buffers. It's annoying.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • damn @jonskeet, ;) i would suggest the generic way as well... your faster ;) – dknaack Oct 17 '11 at 19:08
  • Isn't this pretty much the Option 2 Ross listed as not liking because you would have to implement it explicitly? – Mr.Mindor Oct 17 '11 at 19:41
  • @Mr.Mindor: Sort of - I hadn't noticed that the derived classes didn't want to override the method. There's a better approach then :) – Jon Skeet Oct 17 '11 at 19:50
  • @Jon Skeet Perhaps this belongs in a separate question, but I'm honestly curious. Is there a benefit to using generics in this manner (modified answer) as opposed to an extension method `public static T Method(this T derv) {return derv;}` I can see a benefit in it being explicitly part of the class and therefore clearer that the functionality exists (as opposed to the extension method which would probably be elsewhere in the project) anything else? – Mr.Mindor Oct 18 '11 at 21:44
  • @Mr.Mindor: Presumably with a constraint on T and the relevant method body inside it? Interesting. Yes, I like it. Would have to think about whether there are downsides to that... – Jon Skeet Oct 18 '11 at 21:46
  • oops, yes, I left off the constraint typing it in the comment. Full declaration should be `public static T Method(this T derv) where T:Base {return derv;}` – Mr.Mindor Oct 19 '11 at 02:47
  • @Mr.Mindor: Yes, I like your idea. Very interesting - have upvoted. – Jon Skeet Oct 19 '11 at 05:21
  • @JonSkeet why not use hide-by-signature? See below. – Anton Tykhyy Aug 13 '13 at 19:24
  • @AntonTykhyy: Yes, that would work - but I personally don't think it's terribly nice. It does avoid the cast, mind you. – Jon Skeet Aug 13 '13 at 19:30
  • Yeah, it's not very nice, but it's simple and straightforward: it solves exactly Ross's problem with minimal side effects, I believe. – Anton Tykhyy Aug 13 '13 at 20:14
11

I believe you can use the dynamic keyword on C# 4.0:

public abstract class Base { 
    public dynamic Method() { return this; }
}

public class Derived1: Base { ... }

public class Derived2: Base { ... }

public class Main {
    public static int Main() {
        Derived1 d1 = new Derived1();
        Derived1 x = d1.Method();
        Console.WriteLine(x.GetType()); // outputs Derived1
        Derived2 d2 = new Derived2();
        Derived2 y = d2.Method();
        Console.WriteLine(y.GetType()); // outputs Derived2
    }
}
smoak
  • 14,554
  • 6
  • 33
  • 33
  • +1, fits with the OP's request, (an alternative to the 2 methods he thought of but didn't like.) – Mr.Mindor Oct 17 '11 at 19:48
  • I wish the code in question wasn't .NET 3.5. You get the best answer award, though, because this is exactly what I was looking for. – Ross Patterson Oct 21 '11 at 16:42
  • 3
    `dynamic` is a worse tool of the devil than casts. In this particular case, the compiler and the runtime cooperate in inserting a cast for you. – Anton Tykhyy Aug 13 '13 at 19:19
  • 1
    Worked gloriously. @AntonTykhyy I agree and disagree with you. In this scenario the `public dynamic Method()` is a smell. If `Method()` was protected that would solve that smell. The clearest example of why you would want this is fluent chaining where the base class uses `return this` which would break chaining or require casting in the dervived types unless it was dynamic. – Chris Marisic Dec 10 '14 at 18:22
  • 3
    @ChrisMarisic "_The clearest example of why you would want this is fluent chaining where the base class uses return this_" Which was exactly what I was trying to do at the time. – Ross Patterson Jan 12 '16 at 22:48
8

Seems there are many ways to accomplish this: Also possible to do with Extension Methods.
Which has the benefit of allowing you to do with classes you don't own, and you only have to do it once, not once for each derived/base class. This will do exactly what you are looking for.

public class BaseClass
{

}
public class DerivedClass: BaseClass
{

}
public static class BaseClassHelpers
{
    public static T Method<T>(this T b) where T : BaseClass
    {
        return b;
    }
}    

in use:

DerivedClass d = new DerivedClass();
DerivedClass dd = d.Method();
Console.WriteLine(dd.GetType());

result in console:

DerivedClass
Mr.Mindor
  • 4,079
  • 2
  • 19
  • 25
  • Printing out `dd.GetType()` doesn't really prove much as that's the execution-time type of the object. Your example would be more convincing if you declare `dd` as type `DerivedClass` (which should work, of course). – Jon Skeet Oct 18 '11 at 21:47
  • 2
    This is my preferred solution because it's "safest" - no dynamic keyword and no casting, so it should never fail at runtime. – Ian Kemp Feb 22 '12 at 14:18
5

Also with generics but without the interface:

public abstract class Base<T> where T : Base<T>
{
    public virtual T Method()
    {
        return (T) this;
    }
}

public class Derived1 : Base<Derived1>
{
    public override Derived1 Method()
    {
        return base.Method();
    }
}

public class Derived2: Base<Derived2> { }

public class Program {
public static int Main() {
    Derived1 d1 = new Derived1();
    Derived1 x = d1.Method();
    Derived2 d2 = new Derived2();
    Derived2 y = d2.Method();
    return 0;
}
nemesv
  • 138,284
  • 16
  • 416
  • 359
  • This also is esentially Option 2 that the OP did not like: Requires writing a Method() in each Derived class. – Mr.Mindor Oct 17 '11 at 19:45
  • @Mr.Mindor no it's not required to override Method() in every derived class (it's anyway virtual) see Derived2. I've only provided an implementation in Derived1 to show that the return type is really Derived1. – nemesv Oct 17 '11 at 19:54
  • This is what I'll be using, because I'm constrained to a .NET 3.5 solution at the moment. But when we jump to 4.0, I'll be switching to smoak's instead. But thank you, nemesv! – Ross Patterson Oct 21 '11 at 16:44
1

If your method has parameters of type T, then the explicit generic arguments are not needed.

public class Base
{
    public T Method<T>()where T: Base
    {
        return (T)this;
    }
}

public class Derived1 : Base { }
public class Derived2 : Base { }

Derived1 d1 = new Derived1();
Derived1 x = d1.Method<Derived1>();
Derived2 d2 = new Derived2();
Derived2 y = d2.Method<Derived2>();
Sharpiro
  • 2,305
  • 2
  • 18
  • 27
0

This is a solution without casting

public abstract class Base<T> where T : Base<T>
{
    public T Method()
    {
        return ThisDerived;
    }

    // It is worth to do this if you need the "this" of the derived class often in the base class
    protected abstract T ThisDerived { get; }
}

public class Derived1 : Base<Derived1>
{
    protected override Derived1 ThisDerived{ get { return this; } } 
}

public class Derived2 : Base<Derived2>
{
    protected override Derived2 ThisDerived { get { return this; } }
}
Alex Siepman
  • 2,499
  • 23
  • 31
-1

There is a very simple way of achieving the desired result with the new keyword and hide-by-signature:

class Base
{
    public virtual Base Method () { return this ; }
}

class Derived1: Base 
{ 
    public new Derived1 Method () { return this ; }
}

class Derived2: Base
{
    public new Derived2 Method () { return this ; }
}
Anton Tykhyy
  • 19,370
  • 5
  • 54
  • 56