Constraints in C##
Introduction
Your program do more than store data based on a given type parameter. It is a common thing. And often you want your program to use members of the type parameter to execute statements within your program's generic type. And this is a fact.
Necessity of constraints
Suppose in the Add method of the Dictionary class you want to compare items using the CompareTo method of the supplied key.
See the example:
public class Dictionary<KeyType, ValType> { public void Add(KeyType key, ValType val) { ... switch(key.CompareTo(x)) { } ... } }
Unluckily, at compile-time the type parameter KeyType is, as expected, generic. Written as-is, the compiler assumes that only the operations available to a base object type, such as ToString, are available to the key instance of the type parameter, KeyType. As a result, the compiler displays a compilation error because the CompareTo method is undefined. However, your program can cast the key variable to an object that does contain a CompareTo method, such as an IComparable interface. In the following example, your program explicitly casts the instance of the parameter type KeyType called key to an IComparable interface, allowing the program to gather:
public class Dictionary<KeyType, ValType> { public void Add(KeyType key, ValType val) { ... switch(((IComparable) key).CompareTo(x)) { } ... } }
If you now instantiate a Dictionary class and supply a type parameter that does not implement the IComparable interface, your program will encounter a run-time error, specifically Invalid Cast exclusion.
Constraints affirmation
A constraint indicates a requirement that a type must fulfill in order to construct a generic type. Your program can supply an optional list of constraints for each type parameter declared in your generic class in C#. Constraints are declared using the where keyword, followed by a "parameter-requirement" pair where the "parameter" must be one of those defined in the generic type, and the "requirement" must be a class or crossing point.
For the satisfaction of the need to use the CompareTo method in the Dictionary class, your program can impose a constraint on the KeyType type parameter, requiring any type passed to the first parameter of the Dictionary class to implement the IComparable interface.
Example:
public class Dictionary<KeyType, ValType> where KeyType : IComparable { public void Add(KeyType key, ValType val) { ... switch(key.CompareTo(x)) { } ... } }
your code will be checked to ensure that each time your program uses the Dictionary class when compiled. It is passing as a first parameter a type that implements the IComparable interface. Further, your program no longer has to explicitly cast the variable to an IComparable interface before calling the CompareTo method.
Numerous Constraints
Your program can specify any number of interfaces as constraints, but no more than one class for any given type parameter. Each new constraint is declared as another parameter-requirement pair, with each constraint for a given generic type separated by commas. In the following example, the Dictionary class contains two types of parameters, KeyType and ValType. The KeyType type parameter has two interface constraints, while the ValType type parameter has one class constraint:
public class Dictionary<KeyType, ValType> where KeyType : IComparable, KeyType : IEnumerable, ValType : Customer { public void Add(KeyType key, ValType val) { ... switch(key.CompareTo(x)) { } ... } }
Have a nice time!