10

Yesterday, I was explaining C#'s generic constraints to my friends. When demonstrating the where T : CLASSNAME constraint, I whipped up something like this:

public class UnusableClass<T> where T : UnusableClass<T>
{
    public static int method(T input){
        return 0;
    }
}

And was really surprised to see it compile. After a bit of thinking, however, I figured it was perfectly legal from the point of view of the compiler - UnusableClass<T> is as much of a class as any other that can be used in this constraint.

However, that leaves a couple of questions: how can this class ever be used? Is it possible to

  1. Instantiate it?
  2. Inherit from it?
  3. Call its static method int method?

And, if yes, how?

If any of these is possible, what would the type of T be?

  • 3
    Hmm.. smells like homework. – Jamie Keeling Mar 24 '15 at 09:13
  • 3
    @JamieKeeling: no, not really. I'm just interested if it's even possible =) My friends were totally sure that it's not, but I had my doubts... –  Mar 24 '15 at 09:14
  • I am considering how to improve the title. It's kind of - haha - generic right now... – J. Steen Mar 24 '15 at 09:19
  • @J.Steen: I know, sorry... But I couldn't think of anything else when I was asking =) –  Mar 24 '15 at 09:20
  • Sometimes it's hard finding the right terminology for a piece of code. When you do, feel free to edit the title with the correct phrase so people searching for it can find it in the future. =) – J. Steen Mar 24 '15 at 09:26
  • @J.Steen: is it better now? =) –  Mar 24 '15 at 09:31
  • 1
    @Mints97 Definitely! Nice. – J. Steen Mar 24 '15 at 09:32
  • 4
    It's a good pattern for recursive types, as trees and graphs. It becomes an anti-pattern in pretty much every other case, where it complicates the code and make it less readable. I remember the code base at my previous work, where this was used everywhere. It still makes me shiver. – Falanwe Mar 24 '15 at 09:38
  • 4
    Related: [Curiously recurring template pattern](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) – CodesInChaos Mar 24 '15 at 10:06
  • 3
    Isn't this just F-bounded Polymorphism? – Jörg W Mittag Mar 24 '15 at 11:17
  • For an in the wild [example](https://github.com/MarimerLLC/csla/blob/master/Source/Csla/BusinessBase.cs), the [CSLA](http://www.cslanet.com/) business object framework uses this pattern, for precisely the reason mentioned in J. Steen's answer. – Esoteric Screen Name Mar 24 '15 at 14:10
  • A quick look at https://en.wikipedia.org/wiki/Bounded_quantification might be worthwile. – Martijn Mar 24 '15 at 15:14

3 Answers3

14

This approach is widely used in Trees and other Graph-like structures. Here you say to compiler, that T has API of UnusableClass. That said, you can implement TreeNode as follows:

public class TreeNode<T>
    where T:TreeNode<T>
{
    public T This { get { return this as T;} }

    public T Parent { get; set; }

    public List<T> Childrens { get; set; }

    public virtual void AddChild(T child)
    {
        Childrens.Add(child);
        child.Parent = This;
    }

    public virtual void SetParent(T parent)
    {
        parent.Childrens.Add(This);
        Parent = parent;
    }
}

And then use it like this:

public class BinaryTree:TreeNode<BinaryTree>
{
}
Alex Voskresenskiy
  • 2,143
  • 2
  • 20
  • 29
8

Well.

public class Implementation : UnusableClass<Implementation>
{
}

is perfectly valid, and as such makes

var unusable = new UnusableClass<Implementation>();

and

UnusableClass<Implementation>.method(new Implementation());

valid.

So, yes, it can be instantiated by supplying an inheriting type as the type parameter, and similarly with the call to the static method. It's for instance useful for tree-like structures where you want to generically specify the type of children the node has, while it being the same type itself.

J. Steen
  • 15,470
  • 15
  • 56
  • 63
5

If any of these is possible, what would the type of T be?

They are all possible, and you are the one who is gonna determine what is the type of T.For example let's assume there is a type that inherits from UnusableClass<T>

class Foo : UnusableClass<Foo> { }

Now you can instantiate UnusableClass<Foo> because Foo satisfies the constraint:

UnusableClass<Foo> f = new UnusableClass<Foo>();

Then the type of T become Foo and if you try to call method you need to pass an instance of Foo.

Selman Genç
  • 100,147
  • 13
  • 119
  • 184