Consider the following class:
public class DerivedClassPool<TBase> where TBase : class
{
public TBase Get(Type componentType)
{
// Not important, but you get the idea
return Activator.CreateInstance(componentType) as TBase;
}
public TDerived SomeMethod<TDerived>() where TDerived : TBase
{
return Get(typeof(TBase)) as TDerived;
}
}
Note that I've restricted the TBase
generic class argument to be a class: where TBase : class
I've also restricted the TDerived
generic method argument to be TBase
or something derived from that: where TDerived : TBase
.
I get an error on the as TDerived
line:
The type parameter 'TDerived' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint
I understand that to prevent the error I need to add the constraint class
, so I'd get:
where TDerived : class, TBase
Why do I have to do this when TBase
is already constrained to be a class and TDerived
is constrained to be a TBase
or derived from it?
It will give a compile-time error if you try to instantiate a generic type using a type that is not allowed by the specified constraints. You can specify one or more constraints on the generic type using the where clause after the generic type name.
Interface Type Constraint You can constrain the generic type by interface, thereby allowing only classes that implement that interface or classes that inherit from classes that implement the interface as the type parameter.
NET class library defines several generic interfaces for use with the collection classes in the System. Collections. Generic namespace.
Value type constraint If we declare the generic class using the following code then we will get a compile-time error if we try to substitute a reference type for the type parameter.
UPDATE: This question was the subject of my blog on September 19th, 2011. Thanks for the great question!
Why do i have to do this when TBase is already constrained to be a class and TDerived is constrained to be a TBase or derived from it?
Because a value type can be derived from a reference type. int
is derived from reference types object
and System.ValueType
and implements a number of interfaces. That doesn't make int
a reference type.
It is perfectly legal for you to call SomeMethod<int>
on an instance of DerivedClassPool<object>
because int is derived from object.
Now, there are cases where your criticism would be warranted. One can construct situations in which two type parameters are related in such a way that both of them logically can only be reference types, but only one of them is classified by the language as "known to be of reference type".
As an exercise to the reader: can you find one? It might be necessary to carefully read section 10.1.5 of the specification for a precise definition of "known to be a reference type".
Because TBase
could be an interface and therefore TDerived
could be a value type.
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