16

I first saw a colleague do this when he implemented object pools. He passed the class that was going to be pooled as a parameter to a generic base class. This base class layed out the pooling code.

The odd thing is that the base class will know of its children. This is considered bad practice in every normal case. But in this case the parent is just a technical solution to avoid writing repetetive code. The base class is never referenced by any other code.

One drawback with this construction is that it "burns the base class". You cannot introduce the generic base class in the middle of a hierarchy. This problem might be outside the topic.

Below is a thinkable example:

public abstract class Singleton<T> where T : class
{
    public static T Instance { get; private set; }

    public Singleton()
    {
        if (Instance != null)
            throw new Exception("Singleton instance already created.");
        Instance = (T) (object) this;
    }
}

public class MyClass : Singleton<MyClass>
{
}

Improved code:

public abstract class Singleton<T> where T : Singleton<T>
{
    public static T Instance { get; private set; }

    public Singleton()
    {
        if (Instance != null)
            throw new Exception("Singleton instance already created.");
        Instance = (T) this;
    }
}

public class MyClass : Singleton<MyClass>
{
}
Xaruth
  • 4,034
  • 3
  • 19
  • 26
Johan Nilsson
  • 427
  • 4
  • 10

2 Answers2

14

No; this is a well-known pattern called the CRTP.
It is especially useful in C++ as an alternative to virtual methods.

You can see it inside the .Net framework in IComparable<T> and IEquatable<T>.

For added robustness, you should add where T : Singleton<T>

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • You're blowing my mind with that last sentence. class SingleTon where T: Singleton ? Wouldn't that require you to go endlessly recursive(every T would have to be a Singleton, where that T would again be a singleton , etc etc) – Kristof Oct 08 '13 at 11:56
  • Added SLaks code improvement. The first line does make your head spin. – Johan Nilsson Oct 08 '13 at 11:59
  • @Kristof See the example: `public MyClass: Singleton` above. The type, `MyClass` IS-A `Singleton` so it works. It's still a bit brain-melty though. – Matthew Watson Oct 08 '13 at 12:00
  • After mind processing your example a few times it does add up :) still partly brain fried but yeh, it's correct :) – Kristof Oct 08 '13 at 12:02
  • 2
    Also see the following discussion which asserts that it is NOT the same as the C++ CRTP: http://stackoverflow.com/questions/16142620/what-does-this-parameter-type-constraint-mean – Matthew Watson Oct 08 '13 at 12:03
  • I think the most famous example is from Java, where the ubiquitous `Enum` class is defined as `Enum>`. See also [here](http://madbean.com/2004/mb2004-3/) and [here](http://stackoverflow.com/questions/3061759). – BlueRaja - Danny Pflughoeft Oct 08 '13 at 14:52
3

SLaks is correct - this is a useful pattern, generally for when you want to provide code in your base class which is strongly typed to your derived class.

You would also generally add a type constraint to the generic parameter to indicate that the generic type must inherit from the abstract type. The syntax for adding this constraint looks recursive, but don't panic about that - it is not evaluated recursively and simply ensures that the only valid generic types are derived classes.

For example, let's say you run a tea and coffee blending business. It makes sense for you to blend coffee with coffee, and tea with tea, but you want to make sure that you can't blend coffee with tea. However, as they are both beverages, you want to model them in the same way.

public abstract class Beverage<T> where T : Beverage<T>
{
    public abstract T Blend(T drink1, T drink2);
}

public class Tea : Beverage<Tea>
{
    public override Tea Blend(Tea drink1, Tea drink2)
    { 
        // Blend tea here.
    }
}
public class Coffee : Beverage<Coffee>
{
    public override Coffee Blend(Coffee drink1, Coffee drink2)
    { 
        // Blend coffee here.  Although coffee is nasty, so
        // why you'd want to is beyond me.
    }
}

When reading about CRTP, it is worth remembering that C++ templates are only superficially similar to C# generics. The key difference is that templates are effectively a code generation tool which work at compile-time, whereas C# generics are supported at run time.

Furthermore, writing code like this can reduce readability. So although there are definitely instances where this would be the correct approach, you should take a think about the problem you're trying to solve and see if there's a more straightforward approach.

James Cane
  • 123
  • 1
  • 8
  • MyClass.Instance does compile. – Johan Nilsson Oct 08 '13 at 12:42
  • 2
    Of course, `public class Coffee : Beverage` is a legal declaration. It's easy to mess this up. – Brian Oct 08 '13 at 13:49
  • Yes, although it would be obvious as soon as you tried to use the `Blend()` method and realised that it was expecting a `Tea` type. Still, you're right - it is easy to mess it up. It's also not straightforward to read. – James Cane Oct 08 '13 at 14:02