Any data type can be used with generic classes like int, float, double. However, sometimes it is required to restrict the data types that can be used with particular generic class or methods. This can achieved using generic constraints.
Types of generic constraints
Derivation constraints – These constraints restrict the generic type parameter to be derivative of specified interface or class.
public class LinkedList<K,T> where K : I Comparable
// K data type must implement IComparable
public class LinkedList<K,T> where K : IComparable<K>
// K data type must implement IComparable<T>
public class LinkedList<K,T> : IEnumerable<T> where K : IComparable<K>
// Derive from IEnumerable<T> and K data type must implement IComparable<K>
public class LinkedList<K,T> where K : IComparable<K>,IConvertible
// K data type must implement IComparable<K> and IConvertible
public class LinkedList where K : IComparable,IConvertible
// K data type must implement both IComparable and IConvertible
Constructor Constraints – These constraints restrict the generic type parameter to define default constructor.
Let’s assume that we have a generic class Node that has a property of data type N. While instantiating object of data type Node, we want to instantiate the object of data type N inside Node class. In such a situation, the compiler will try to invoke default constructor of the class N.
If the default constructor is not defined for class N, it will throw exception while instantiating object of N. One can restrict the generic data type to have default constructor.
class Node<K,T> where T : new() // T must have default constructor
public K Key;
public T Item;
public Node<K,T> NextNode;
Key = default(K);
Item = new T();
NextNode = null;
public class LinkedList<K,T>
where K : IComparable<K>,new() // K must implement Icomparable<K> and have default constructor Reference/Value Type Constraint Sometimes, it is required to define a constraint so that the generic data type is value type or reference type.
One can restrict a generic type parameter to be a value type (such as an int, a bool, and enum) or any custom structure using the struct constraint:
public class MyClass where T : struct
Similarly, one can constrain a generic type parameter to be a reference type (a class) using the class constraint:
public class MyClass where T : class
C# generics are syntactically similar to C++ templates, but there are some important differences in the way they are implemented and supported by the compiler.
As compared with C++ templates, C# generics provide enhanced safety but are also limited in capabilities. Generics in .NET 2.0, have native support in IL (intermediate language) and the CLR itself.
When generic C# server-side code is compiled, the compiler compiles it into IL, as for any other type. The IL only contains parameters or place holders for the actual specific types. The metadata of the generic server contains generic information.
Generic metadata is used by the client side compiler to support type safety. When client provides a specific type parameter, the client’s compiler replaces the generic type parameter in the server metadata with the specified type argument. This provides the client’s compiler with type-specific definition of the server, as if generics were never involved. This way the client compiler can enforce correct method parameters, type-safety checks, and even type-specific IntelliSense.
Process of compiling generic IL of server to machine code. The actual machine code produced depends on whether the specified types are value or reference type. If it is of value type, then the JIT compiler replaces the generic type parameters in the IL with the specific value type, and compiles it to native code. JIT compiler also keeps the record of type-specific server code it already generated. If compiler is asked to compile the generic server with a value type which it has already compiled to machine code, it simply returns a reference to that server code. Because the JIT compiler uses the same value-type-specific server code in all further encounters, there is no code bloating.
If the client specifies a reference type, then the JIT compiler replaces the generic parameters in the server IL with Object, and compiles it into native code. That code will be used in any further request for a reference type instead of a generic type parameter. Note that this way the JIT compiler only reuses actual code. Instances are still allocated according to their size off the managed heap, and there is no casting.
Generics in .NET let you reuse code and the effort you put into implementing it. The types and internal data can change without causing code bloat, regardless of whether you are using value or reference types. You can develop, test, and deploy your code once, reuse it with any type, including future types, all with full compiler support and type safety. Because the generic code does not force the boxing and un-boxing of value types, or the down casting of reference types, performance is greatly improved. With value types there is typically a 200 percent performance gain, and with reference types you can expect up to a 100 percent performance gain in accessing the type (of course, the application as a whole may or may not experience any performance improvements). The source code available with this article includes a micro-benchmark application, which executes a stack in a tight loop. The application lets you experiment with value and reference types on an Object-based stack and a generic stack, as well as changing the number of loop iterations to see the effect generics have on performance.
For more details and queries please feel free to email, visit or call us. Wishing you the very best for all your future endeavors.
Helpline: 9814666333, 8699444666
Please fill the form and we shall contact you soon.