The constraint clause consists of where followed by a type parameter, a colon (:), and the constraint, which specifies the nature of the restriction on the type parameter. where is a context-sensitive keyword; see Context-Sensitive Keywords for more information. Separate multiple where clauses with a space.
Constraints are applied to type parameters to place limitations on the types that can be used as arguments for a generic type or method.
Class and interface constraints specify that the argument types must be or inherit from a specified class or implement a specified interface.
The application of constraints to a generic type or method allows code in that type or method to take advantage of the known features of the constrained types. For example, you can declare a generic class such that the type parameter implements the IComparable<T> interface:
// generics_constraints_1.cpp
// compile with: /c /clr
using namespace System;
generic <typename T>
where T : IComparable<T>
ref class List {};
This constraint requires that a type argument used for T implements IComparable<T> at compile time. It also allows interface methods, such as CompareTo, to be called. No cast is needed on an instance of the type parameter to call interface methods.
Static methods in the type argument's class cannot be called through the type parameter; they can be called only through the actual named type.
A constraint cannot be a value type, including built-in types such as int or double. Since value types cannot have derived classes, only one class would ever be able to satisfy the constraint. In that case, the generic can be rewritten with the type parameter replaced by the specific value type.
Constraints are required in some cases since the compiler will not allow the use of methods or other features of an unknown type unless the constraints imply that the unknown type supports the methods or interfaces.
Multiple constraints for the same type parameter can be specified in a comma-separated list
// generics_constraints_2.cpp
// compile with: /c /clr
using namespace System;
using namespace System::Collections::Generic;
generic <typename T>
where T : List<T>, IComparable<T>
ref class List {};
With multiple type parameters, use one where clause for each type parameter. For example:
// generics_constraints_3.cpp
// compile with: /c /clr
using namespace System;
using namespace System::Collections::Generic;
generic <typename K, typename V>
where K: IComparable<K>
where V: IComparable<K>
ref class Dictionary {};
To summarize, use constraints in your code according to the following rules:
If multiple constraints are listed, the constraints may be listed in any order.
Constraints can also be class types, such as abstract base classes. However, constraints cannot be value types or sealed classes.
Constraints cannot themselves be type parameters, but they can involve the type parameters in an open constructed type. For example:
// generics_constraints_4.cpp
// compile with: /c /clr
generic <typename T>
ref class G1 {};
generic <typename Type1, typename Type2>
where Type1 : G1<Type2> // OK, G1 takes one type parameter
ref class G2{};