Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does 'Classname<T> where T: Classname<T>' do?

Tags:

c#

generics

I was reading the german Wikipedia article about the prototype pattern. The example section contained a generic C# implementation using the following:

abstract class Prototype<T> where T : Prototype<T> { ... }
...
class ConcretePrototype : Prototype<ConcretePrototype> { ... }

How does this work? How can you restrict T to the same generic class? How can you use a class to derive from a generic type using itself?

I am not programming C#, but this one seems interesting.

Source: Prototype Pattern

like image 673
Luca Fülbier Avatar asked Dec 01 '15 13:12

Luca Fülbier


3 Answers

ProtoType<T> has a Clone method, that returns the concrete prototype in a type-safe fashion, therefore T must be defined as a type parameter. As the type of T must only be a class derived from Prototype, then the line:

abstract class Prototype<T> where T : Prototype<T> { ... }

is required to constrain T to only being a subclass of Prototype. As Prototype is generic, Prototype<T> must be specified in the constraint.

In theory, the declaration for ConcretePrototype ought to just be:

class ConcretePrototype : Prototype<> { ... }

(or similar syntax). But the C# compiler doesn't support inferring type parameters in this way. If you put something like:

class ConcretePrototype : Prototype<string> { ... }

you'll get a compilation error, as it knows it must be Prototype<ConcretePrototype>, due to the way Prototype is constrained. The compiler requires an explicit declaration of this though, thus:

class ConcretePrototype : Prototype<ConcretePrototype> { ... }

I notice that Damien_The_Unbeliever beat me to finding the reference, but I'll mention Eric Lippert's excellent post on this topic anyway. It's definitely worth reading to both help understand it and understand why it can cause problems.

like image 148
David Arno Avatar answered Nov 15 '22 06:11

David Arno


Well, basically this just restricts the TypeParameter T to be a type inheriting from Prototype with its own type as a TypeParameter.

So, only classes inheriting from Prototype<T> can be passed as T.

(Maybe) Working Example:

class FirstConcretePrototype : Prototype<FirstConcretePrototype> { } // works

// propably not what the author wanted to happen but...
class SecondConcretePrototype : Prototype<FirstConcretePrototype> { } // works (at least compiles) too, funny huh?

Be aware that the SecondConcretePrototype is valid C#, but would probably fail, because T is FirstConcretePrototype and in Prototype<T>s Clone-method, the this-object (which is of Type SecondConcretePrototype) gets casted to FirstConcretePrototype. Given this cast is not possible, it will always fail at runtime, because in SecondConcretePrototype

public T Clone()
{
    return (T)this.MemberwiseClone();
}

translates to

// still in SecondConcretePrototype ...
public FirstConcretePrototype Clone()
{
    return (FirstConcretePrototype)this.MemberwiseClone(); // 'this' is of type SecondConcretePrototype 
}

I know this is nothing one sane man would ever type, but it is worth pointing it out and makes this pattern somewhat "unclean" IMO, because the Typerestriction doesn't protect you from doing crappy things..

Failing Example

class AnyType{ }

class ThirdConcretePrototype : Prototype<AnyType> { } // fails at compiletime, AnyType does not inhertit from Prototype<T>
like image 41
nozzleman Avatar answered Nov 15 '22 06:11

nozzleman


I'll try to explain it but first let's have a look at short but working example :

abstract class Prototype<T> where T : Prototype<T> 
{
    public T Clone()
    {
        return this.MemberwiseClone() as T;
    }
}

class ConcretePrototype1 : Prototype<ConcretePrototype1> 
{
    public int Id { get; set; }
    public string Name { get; set; }
}

class ConcretePrototype2 : Prototype<ConcretePrototype2> 
{
    public int Id { get; set; }
    public string Name { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        ConcretePrototype1 inst1 = new ConcretePrototype1()
        {
            Id = 1,
            Name = "Jon Skeet"
        };

        ConcretePrototype2 inst2 = new ConcretePrototype2()
        {
            Id = 2,
            Name = "Frodo Torbins"
        };

        ConcretePrototype1 copy1 = inst1.Clone();
        ConcretePrototype2 copy2 = inst2.Clone();

        Console.WriteLine(copy1.Name + "  " + copy1.GetType().Name);
        Console.WriteLine(copy2.Name + "  " + copy2.GetType().Name);
    }
} 

// Output

Jon Skeet   ConcretePrototype1
Frodo Torbins ConcretePrototype2

Explanation :

What does this work?

As you can see prototype pattern has only one method Clone() which produces copy of current object.

How can you restrict T to the same generic class?

There is no reason why you couldn't restrict type parameter to same class or derived class that inherits from this base abstract class. This will result in something like : Prototype<Prototype<T>> or Prototype<Derived<T>> (both works assuming that Derived inherits from Prototype class)

How can you use a class to derive from a generic type using itself?

When we declare ConcretePrototype1 class we derive it from Prototype<ConcretePrototype1> (itself) so we let compiler know that Prototype<T> pattern should use ConcretePrototype1 as its T parameter. This leads to logic where method Clone() returns instance of ConcretePrototype1 because it's what our T is. Same logic is used for ConcretePrototype2 class.

So in short, this abstract class signature for Prototype pattern :

abstract class Prototype<T> where T : Prototype<T> 

restricts its Clone() method to only produce instances of derived classes and nothing more.

like image 31
Fabjan Avatar answered Nov 15 '22 07:11

Fabjan