C# Generic Constraints
C# allows you to use constraints to restrict client code to specify certain types while instantiating generic types. It will give a compile-time error if you try to instantiate a generic type using a type that is not allowed by the specified constraints.
You can specify one or more constraints on the generic type using the where
clause after the generic type name.
GenericTypeName<T> where T : contraint1, constraint2
The following example demonstrates a generic class with a constraint to reference types when instantiating the generic class.
class DataStore<T> where T : class
{
public T Data { get; set; }
}
Above, we applied the class constraint, which means only a reference type can be passed as an argument while creating the DataStore class object. So, you can pass reference types such as class, interface, delegate, or array type. Passing value types will give a compile-time error, so we cannot pass primitive data types or struct types.
DataStore<string> store = new DataStore<string>(); // valid
DataStore<MyClass> store = new DataStore<MyClass>(); // valid
DataStore<IMyInterface> store = new DataStore<IMyInterface>(); // valid
DataStore<IEnumerable> store = new DataStore<IMyInterface>(); // valid
DataStore<ArrayList> store = new DataStore<ArrayList>(); // valid
//DataStore<int> store = new DataStore<int>(); // compile-time error
The following table lists the types of generic constraints.
Constraint | Description |
---|---|
class | The type argument must be any class, interface, delegate, or array type. |
class? | The type argument must be a nullable or non-nullable class, interface, delegate, or array type. |
struct | The type argument must be non-nullable value types such as primitive data types int, char, bool, float, etc. |
new() | The type argument must be a reference type which has a public parameterless constructor. It cannot be combined with struct and unmanaged constraints.
|
notnull | Available C# 8.0 onwards. The type argument can be non-nullable reference types or value types. If not, then the compiler generates a warning instead of an error. |
unmanaged | The type argument must be non-nullable unmanged types. |
base class name | The type argument must be or derive from the specified base class. The Object, Array, ValueType classes are disallowed as a base class constraint. The Enum, Delegate, MulticastDelegate are disallowed as base class constraint before C# 7.3. |
<base class name>? | The type argument must be or derive from the specified nullable or non-nullable base class |
<interface name> | The type argument must be or implement the specified interface. |
<interface name>? | The type argument must be or implement the specified interface. It may be a nullable reference type, a non-nullable reference type, or a value type |
where T: U | The type argument supplied for T must be or derive from the argument supplied for U. |
where T : struct
The following example demonstrates the struct
constraint that restricts type argument to be non-nullable value type only.
class DataStore<T> where T : struct
{
public T Data { get; set; }
}
DataStore<int> store = new DataStore<int>(); // valid
DataStore<char> store = new DataStore<char>(); // valid
DataStore<MyStruct> store = new DataStore<MyStruct>(); // valid
//DataStore<string> store = new DataStore<string>(); // compile-time error
//DataStore<IMyInterface> store = new DataStore<IMyInterface>(); // compile-time error
//DataStore<ArrayList> store = new DataStore<ArrayList>(); // compile-time error
where T : new()
The following example demonstrates the struct
constraint that restricts type argument to be non-nullable value type only.
class DataStore<T> where T : class, new()
{
public T Data { get; set; }
}
DataStore<MyClass> store = new DataStore<MyClass>(); // valid
DataStore<ArrayList> store = new DataStore<ArrayList>(); // valid
//DataStore<string> store = new DataStore<string>(); // compile-time error
//DataStore<int> store = new DataStore<int>(); // compile-time error
//DataStore<IMyInterface> store = new DataStore<IMyInterface>(); // compile-time error
where T : baseclass
The following example demonstrates the base class
constraint that restricts type argument to be a derived class of the specified class, abstract class, or an interface.
class DataStore<T> where T : IEnumerable
{
public T Data { get; set; }
}
DataStore<ArrayList> store = new DataStore<ArrayList>(); // valid
DataStore<List> store = new DataStore<List>(); // valid
//DataStore<string> store = new DataStore<string>(); // compile-time error
//DataStore<int> store = new DataStore<int>(); // compile-time error
//DataStore<IMyInterface> store = new DataStore<IMyInterface>(); // compile-time error
//DataStore<MyClass> store = new DataStore<MyClass>(); // compile-time error