Base class constraint on generic class specifying the class itself
Asked Answered
F

3

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?

Felty answered 24/3, 2015 at 9:9 Comment(12)
Hmm.. smells like homework.Unicellular
@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...Felty
I am considering how to improve the title. It's kind of - haha - generic right now...Sands
@J.Steen: I know, sorry... But I couldn't think of anything else when I was asking =)Felty
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. =)Sands
@J.Steen: is it better now? =)Felty
@Mints97 Definitely! Nice.Sands
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.Winkler
Related: Curiously recurring template patternIronist
Isn't this just F-bounded Polymorphism?Britzka
For an in the wild example, the CSLA business object framework uses this pattern, for precisely the reason mentioned in J. Steen's answer.Hawley
A quick look at en.wikipedia.org/wiki/Bounded_quantification might be worthwile.Gwenore
S
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.

Sands answered 24/3, 2015 at 9:13 Comment(0)
A
15

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>
{
}
Acrilan answered 24/3, 2015 at 9:16 Comment(0)
S
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.

Sands answered 24/3, 2015 at 9:13 Comment(0)
I
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.

Inaptitude answered 24/3, 2015 at 9:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.