Similar to a non-generic type, the compiled representation of a generic type is intermediate language (IL) instructions and metadata. The representation of the generic type of course also encodes the existence and use of type parameters.
The first time an application creates an instance of a constructed generic type, such as Stack<int>, the just-in-time (JIT) compiler of the .Net Common Language Runtime converts the generic IL and metadata to native code, substituting actual types for type parameters in the process. Subsequent references to that constructed generic type then use the same native code. The process of creating a specific constructed type from a generic type is known as a generic type instantiation.
The .Net Common Language Runtime creates a specialized copy of the native code for each generic type instantiation with a value type, but shares a single copy of the native code for all reference types (since, at the native code level, references are just pointers with the same representation).
Commonly, a generic class will do more than just store data based on a type parameter. Often, the generic class will want to invoke methods on objects whose type is given by a type parameter. For example, an Add method in a Dictionary<K,V> class might need to compare keys using a CompareTo method:
public class Dictionary<K,V> { public void Add(K key, V value) { ...
if (key.CompareTo(x) < 0) {...} // Error, no CompareTo method ... } }
Since the type argument specifIEd for K could be any type, the only members that can be assumed to exist on the key parameter are those declared by type object, such as Equals, GetHashCode, and ToString; a compile-time error therefore occurs in the example above. It is of course possible to cast the key parameter to a type that contains a CompareTo method. For example, the key parameter could be cast to IComparable:
public class Dictionary<K,V> { public void Add(K key, V value) { ...
if (((IComparable)key).CompareTo(x) < 0) {...} ... } }
While this solution works, it requires a dynamic type check at run-time, which adds overhead. It furthermore defers error reporting to run-time, throwing an InvalidCastException if a key doesn’t implement IComparable.
To provide stronger compile-time type checking and reduce type casts, C# permits an optional list of constraints to be supplied for each type parameter. A type parameter constraint specifIEs a requirement that a type must fulfill in order to be used as an argument for that type parameter. Constraints are declared using the Word where, followed by the name of a type parameter, followed by a list of class or interface types, or the constructor constraint new().
In order for the Dictionary<K,V> class to ensure that keys always implement IComparable, the class declaration can specify a constraint for the type parameter K:
public class Dictionary<K,V> where K: IComparable { public void Add(K key, V value) { ...
if (key.CompareTo(x) < 0) {...} ... } }
Given this declaration the compiler will ensure that any type argument supplIEd for K is a type that implements IComparable. Furthermore, it is no longer necessary to explicitly cast the key parameter to IComparable before calling the CompareTo method; all members of a type given as a constraint for a type parameter are directly available on values of that type parameter type.
For a given type parameter, it is possible to specify any number of interfaces as constraints, but no more than one class. Each constrained type parameter has a separate where clause. In the example below, the type parameter K has two interface constraints, while the type parameter E has a class constraint and a constructor constraint:
public class EntityTable<K,E> where K: IComparable<K>, IPersistable where E: Entity, new() { public void Add(K key, E entity) { ...
if (key.CompareTo(x) < 0) {...} ... } }
The constructor constraint, new(), in the example above ensures that a type used as a type argument for E has a public, parameterless constructor, and it permits the generic class to use new E() to create instances of that type.
上面例子中的構建約束new(),確保作為類型參數E的類型有一個公共的,無參數的構建函數,且它允許泛型類 使用 new E() 去創建該類型的實例。
Type parameter constrains should be used with care. While they provide stronger compile-time type checking and in some cases improve performance, they also restrict the possible uses of a generic type. For example, a generic class List<T> might constrain T to implement IComparable such that the list’s Sort method can compare items. However, doing so would preclude use of List<T> for types that don’t implement IComparable, even if the Sort method is never actually called in those cases.