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
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.
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
.
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..
class AnyType{ }
class ThirdConcretePrototype : Prototype<AnyType> { } // fails at compiletime, AnyType does not inhertit from Prototype<T>
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With