Generics make it easy to reuse the code for different data types.This is possible because of type parameters.Type parameter is placeholder for the actual data type which is specified using the type argument when the generic type is instantiated.
Though we can specify different data types as the type arguments but sometimes it is required to restrict the data types which can specified as the type arguments.We can restrict the data types using the constraints.
The different types of constraints are
- Reference type constraint The type argument supplied for the type parameter should be a reference type .In the following example we are applying the class constraint to the GenericUser class.We are restricting the type parameter to the User class ,so only the User class object can be specified for the GenericUser class type parameter
public class GenericUser<T> where T : User { T user; } public class User { public string UserName { get; set; } public User(string _userName) { UserName = _userName; } public void Hello() { Console.WriteLine("Hello {0}", UserName); } }
- Value type constraint The type argument should be a value type.In the following example we are restricting the type argument to a value type.So the only value types such as int or decimal can be used as a type argument for the GenericValue class.
public class GenericValue<T> where T : struct { T value1; }
- Base Class constraint The type argument should derive from the base class.
public class Manager<T> where T : Employee { T emplyee; } public class Employee { }
In the above example as we are declaring a generic Manager class we are deriving it from the Employee class.Since Manager is a type of employee so we want to restrict type argument to the Employee type.
- Interface constraint The type argument should implement a specific interface.In the following example the generic class Student restricts the type parameter to implement the IComparable interface.This will allow the Students to be compared if we are inserting the Student objects in a list .So only the data types implementing the IComparable interface can be used as the type arguments.
public class Student<T> where T : IComparable { }
- Default Parameter constraint The type argument should have public parameterless constructor.This is specified using new() as:
public class Generic<T> where T : new() { T obj=new T(); }
This constraint must be the last one in list if more than one constraints are specified.
The main advantage of constraints is that the compiler is aware of a class member such as a method .If we try to call a class method which we supply as the type argument our code will not compile.So the following code will fail
public class GenericUser<T> { T user; public GenericUser() { user.Hello(); } }
If we want to use a method which belongs to a particular data type in our generic class then we can apply the class constraint.So we can use the method of a data type as:
public class GenericUser<T> where T : User,new() { T user; public GenericUser() { user = new T(); user.Hello(); } } public class User { public User() { } public string UserName { get; set; } public User(string _userName) { UserName = _userName; } public void Hello() { Console.WriteLine("Hello {0}", UserName); } }
In the above generic class as we are restricting the type arguments to the User class hence the compiler is aware of the presence of the Hello() method.
So as we have seen constraints in Generics restricts the type parameters to specific data types such as class or interface.They help the compiler know about the presence of data type members such as methods.