Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is usage of this recuring generic class?

Tags:

c#

generics

This code is compiled in visual studio, what is it's usage

public class MyClass<T>
   where T : MyClass<T>

Note where T : MyClass<T>

like image 384
Reza Avatar asked Mar 20 '23 20:03

Reza


2 Answers

This is the recurring template pattern and is usually used so that a base class can refer to its real type statically. This is done in an attempt to preserve type-safety so that parameter or return values referred to in the base class track the current type in the hierarchy e.g

public class Animal<T> where T : Animal<T>
{
    public abstract T GiveBirth();
}

public class Cat : Animal<Cat>
{
    public override Cat GiveBirth() { return new Cat(); }
}

Without the type parameter the Animal base class method would only be able to define the return type of GiveBirth to be Animal, which may reduce type safety for the clients.

It may be acceptible if you control the entire hierarchy and can ensure that classes supply the correct type parameter, but note that it can be abused e.g.

public class Cat : Animal<Dog> { ... }

Another downside is that any clients need to take account of the generic type parameter if they want to be applied to the base class e.g.

public static void Feed<T>(Animal<T> animal) where T : Animal<T> { ... }
public static void Feed<T>(T animal) where T : Animal<T> { ... }
like image 65
Lee Avatar answered Mar 28 '23 14:03

Lee


This is an example of the curiously recurring pattern. Eric Lippert has an excellent article on this, including why you should usually avoid it.

It might be extended like this:

public class MyChild : MyClass<MyChild>

The pattern doesn't really clue you as to why you want this generic. This is unlike most generics/constraints...e.g. if I have List<Giraffe> I can see the relationship; if I have MyGeneric<T, U> where T : IComparer<U>, I can see what T will do. With T : MyClass<T>, I really have no hints as to the relationships or usages here. Perhaps there's a...

abstract T Instance { get; }

...that you wish to have the stronger-typing of MyChild in the case of MyChild.

As an example of why this isn't so good, you could have MyOtherClass : MyClass<MyChild>, or you could have MyGrandchild : MyChild, neither of which are probably what you were trying to enforce.

like image 37
Tim S. Avatar answered Mar 28 '23 13:03

Tim S.