11

I have a base abstract class that also implements a particular interface.

public interface IMovable<TEntity, T>
    where TEntity: class
    where T: struct
{
    TEntity Move(IMover<T> moverProvider);
}

public abstract class Animal : IMovable<Animal, int>
{
    ...

    public virtual Animal Move(IMover<int> moverProvider)
    {
        // performs movement using provided mover
    }
}

Then I have inherited classes some of which have to override interface implementation methods of the base class.

public class Snake : Animal
{
    ...

    public override Animal Move(IMover<int> moverProvider)
    {
        // perform different movement
    }
}

My interface methods return the same object instance after it's moved so I can use chaining or do something directly in return statement without using additional variables.

// I don't want this if methods would be void typed
var s = GetMySnake();
s.Move(provider);
return s;

// I don't want this either if at all possible
return (Snake)GetMySnake().Move(provider);

// I simply want this
return GetMySnake().Move(provider);

Question

As you can see in my example my overrides in child class returns base class type instead of running class. This may require me to cast results, which I'd like to avoid.

How can I define my interface and implementations so that my overrides will return the actual type of the executing instance?

public Snake Move(IMover<int> moverProvider) {}
Robert Koritnik
  • 103,639
  • 52
  • 277
  • 404
  • 2
    may be simpler re-organize classes and do something like: return provider.Move(GetMySnake()); in this case Move() will return the same object as a parameter and you can use generic approach if needed? – KoViMa May 28 '14 at 12:06
  • 1
    See [Curiously Recurring Template Pattern](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) and why [you probably shouldn't do it](http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx) – Rik May 28 '14 at 12:15

5 Answers5

3

I suggest changing the return type of the interface method to void and moving the chaining behaviour to an extension method where you can get the real type of the target e.g.

public interface IMovable<TEntity, T>
    where TEntity : class
    where T : struct
{
    void MoveTo(IMover<T> moverProvider);
}

public abstract class Animal : IMovable<Animal, int>
{
    public virtual void MoveTo(IMover<int> mover) { }
}

public static class AnimalExtensions
{
    public static TAnimal Move<TAnimal>(this TAnimal animal, IMover<int> mover) where TAnimal : Animal, IMovable<TAnimal, int>
    {
        animal.MoveTo(mover);
        return animal;
    }
}

Note you can make the Move extension more generic if you need it to apply more generally:

public static TEntity Move<TEntity, T>(this TEntity entity, IMover<T> mover) where TEntity : IMovable<TEntity, T> where T : struct
{
    entity.MoveTo(mover);
    return entity;
}
Lee
  • 142,018
  • 20
  • 234
  • 287
  • 1
    I have many different base abstract classes that would require extension methods then... Not really what I'm striving for... – Robert Koritnik May 28 '14 at 12:15
  • @RobertKoritnik That implies that you are able to write this once and re-use many times using some other solution. If you have many different abstract base classes, whatever solution you provide for one will need replicating anyway, unless they all derived from some master object, at which point I'd back away from the object hierarchy and rethink. – Adam Houldsworth May 28 '14 at 12:21
  • @RobertKoritnik - If you need to do this with more than animals you can make the `Move` extension method more general - see update. There's no general way to restrict a method's return type to the receiver's type, the closest you can get is the recurring template pattern. This has the drawback of adding a generic parameter to every type in the hierarchy and can quickly get complicated so I prefer moving the generics outside to another method as shown here. This can cause generic type inference to fail however. – Lee May 28 '14 at 12:22
  • I now implemented your solution. I'm still having some problems but that will become a new question... – Robert Koritnik May 30 '14 at 07:23
2

You can convert Animal to a generic type that accepts the concrete type as a type parameter:

public abstract class Animal<T> : IMovable<T, int> where T:Animal<T>        
{


    public virtual T Move(IMover<int> moverProvider)
    {
    ...
    }
}

public class Snake : Animal<Snake>
{


    public override Snake Move(IMover<int> moverProvider)
    {
    ...
    }
}
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • That allows for one layer, what happens to `public class SideWinder: Snake` ? – James May 28 '14 at 12:07
  • This is usually paired with the self-referencing generic constraint `where T : Animal`. – Adam Houldsworth May 28 '14 at 12:08
  • 3
    @JamesBarrass The same, you have just stated that `Snake` is now a base class so can do `Snake : Animal`. – Adam Houldsworth May 28 '14 at 12:08
  • @AdamHouldsworth fixed. I was actually looking how to write that very thing – Panagiotis Kanavos May 28 '14 at 12:10
  • @AdamHouldsworth but now you have to add `` to `Snake`'s definition in order to inherit? surely that's not right to edit the base class to help define the derived class? What if you also want a new Snake? what do you give it? – James May 28 '14 at 12:11
  • This is called the Curiously Recurring Template Pattern and is [advised against](http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx) – Rik May 28 '14 at 12:12
  • @JamesBarrass this isn't about inheritance, it's a syntax quirk for defining generic classes. – Panagiotis Kanavos May 28 '14 at 12:13
  • @JamesBarrass You are defining your model as a need to be able to specify specific snakes. In this case, the recurring template pattern becomes slightly more unsustainable than the original use case, there are only a very small handful of situations where it is useful. I tend to not use it. As for the non-generic Snake case, you can simply define a derived class that is `UnknownSnake` or `GeneralSnake` to inherit `Snake`, though by this point I would be abandoning the self-referencing constraint. – Adam Houldsworth May 28 '14 at 12:16
  • 1
    @PanagiotisKanavos: you returned `null` in base abstract class method implementation. If I return `this` I need to cast it... :( – Robert Koritnik May 28 '14 at 12:16
  • @Rik indeed, but it's probably the only way to answer the specific question. Most likely, the class hierarchy should be refactored to work in a better way – Panagiotis Kanavos May 28 '14 at 12:17
  • @PanagiotisKanavos: What kind of refactoring would you suggest? – Robert Koritnik May 28 '14 at 12:18
  • @RobertKoritnik Simply so I can compile a sample. – Panagiotis Kanavos May 28 '14 at 12:18
  • @PanagiotisKanavos: My comment didn't include a question which is: How can I also avoid casting when returning `this` which is what I'm doing in all of these as I want call chaining? – Robert Koritnik May 28 '14 at 12:20
  • 1
    @PanagiotisKanavos I agree, but I think it's important mention this pattern has its pitfalls for those that may not be aware of it. – Rik May 28 '14 at 12:21
  • 1
    @Rik the article you point to actually highlights this particular use as one of the places where it solves the task at hand – Rune FS May 28 '14 at 12:26
  • 1
    @RuneFS except that it's still possible to create a `Snake : Animal` which is weird, and it's not a very pretty design to look at, still. – Rik May 28 '14 at 12:36
  • 1
    @PanagiotisKanavos: Another problem. What if I have an entity `Person` that has a property `Pet` which can be any animal. How do I set its type? I can't say `Animal>`? I'd need to provide these types as generic types on `Person` as well. And this may become rather complex if it has several different such typed properties... I would still like to know an alternative you implied earlier... – Robert Koritnik May 28 '14 at 12:52
1

How about:

public virtual T Move<T>(IMover<int> moverProvider) where T : Animal
{
    // performs movement using provided mover
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
1

Sometimes you need to have current type as method return value and it has to change in derived classes. I'd avoid this pattern because it'll lead to strange behaviors and unusual syntax (if your model becomes complex) but give it a try (primary because for very small hierarchies it looks pretty simple):

abstract class Animal<TConcrete> : IMovable<TConcrete, int>
where TConcrete : Animal<T>
{
    public virtual T Move(IMover<int> moverProvider) {
        return (T)this; // Cast to Animal<T> to T isn't implicit
    }
}

sealed class Snake : Animal<Snake>
{
    public virtual Snake Move(IMover<int> moverProvider) {
        return this;
    }
}

Why is this bad? You can answer yourself when you'll need to declare a generic variable of type Animal<TConcrete> (in practice this stops you to have a variable with that base class).

What I'd do is to make this requirement clear (with a class or an extension method - in this case using another name):

abstract class Animal : IMovable<Animal, int>
{
    // Please note that this implementation is explicit
    Animal IMovable<Animal, int>.Move(IMover<int> moverProvider) {
        return MoveThisAnimal(moverProvider);
    }

    protected virtual Animal MoveThisAnimal(IMover<int> moverProvider) {
        // Peform moving
        return this;
    }
}

class Snake : Animal
{
    public Snake Move(IMover<int> moverProvider) {
        return (Snake)MoveThisAnimal(moverProvider);
    }

    protected override Animal MoveThisAnimal(IMover<int> moverProvider) {
        // Peform custom snake moving
        return this;
    }
}
Adriano Repetti
  • 65,416
  • 20
  • 137
  • 208
0

It's messy, but by introducing a non-generic base interface, an extension method can give the desired result. It can also be simplified (to remove the second explicit interface implementation) if you don't care about exposing the 'MoveFunc' to callers:

public interface IMovable
{
    IMovable MoveFunc();
}

public interface IMovable<TEntity, T> : IMovable
    where TEntity : IMovable
{
    new TEntity MoveFunc();
}

public abstract class Animal : IMovable<Animal, int>
{
    protected virtual Animal MoveFunc()
    {
        // performs movement using provided mover
        Debug.WriteLine("Animal");
    }

    Animal IMovable<Animal, int>.MoveFunc()
    {            
        return MoveFunc();
    }

    IMovable IMovable.MoveFunc()
    {
        return ((IMovable<Animal, int>)this).MoveFunc();
    }
}

public class Snake : Animal
{
    protected override Animal MoveFunc()
    {
         // performs movement using provided mover
         Debug.WriteLine("Snake");
    }
}

public static class IMovableExtensions
{
    public static TOut Move<TOut>(this TOut entity) where TOut : IMovable
    {
        return (TOut)entity.MoveFunc();
    }

}

...

Snake snake = new Snake();

Snake moved = snake.Move(); // "Snake"

Animal animal = snake;

animal.Move() // "Snake"
Andrew Hanlon
  • 7,271
  • 4
  • 33
  • 53