Summary: in this tutorial, you’ll learn about C# generic constraints that allow you to specify what kinds of types are acceptable as arguments.
Introduction to C# generic constraints
The following example defines a generic class Result<T>
:
class Result<T>
{
T Data { get; set; }
int Status { get; set; }
public Result(T data, int status)
{
Data = data;
Status = status;
}
}
Code language: C# (cs)
In this example, the Result<T>
only assigns data
argument to Data
member. It doesn’t do anything else that requires the operations of the type T
.
The reason is that the Result<T>
doesn’t know the data of the type T
it will store. Therefore, it can’t know the members of the type T
.
Since all objects are derived from the object
class including T
, the Result<T>
class only knows the members of the object
class such as ToString()
, Equals()
, and GetType()
.
As long as the Result<T>
class does not access the member of the type T
other than members of the object
class, the Result<T>
class can handle any type. This type parameter is called the unbounded type parameter.
However, if you try to use any other members, the compiler will produce an error message. For example:
class Result<T>
{
T Data { get; set; }
int Status { get; set; }
public Result(T data, int status)
{
Data = data;
Status = status;
}
public string JSON()
{
return Data?.ToJSON(); // ERROR
}
}
Code language: C# (cs)
This example causes an error because the JSON()
method of the Result<T>
class attempts to call the ToJSON()
method of the Data
member.
To make the Result<T>
class more useful, you can specify the information about the kind of types that is acceptable as arguments. This additional information is called generic constraints.
To specify a generic constraint, you use the where
clause after the closing angle bracket of the type parameter list:
class MyClass<T> where TypeParam: constraint, constraint,
Code language: C# (cs)
For example, the following specifies that the type T must be a type that implements the IJSON
interface:
interface IJSON
{
string ToJSON();
}
class Result<T>
where T : IJSON
{
T Data { get; set; }
int Status { get; set; }
public Result(T data, int status)
{
Data = data;
Status = status;
}
public string JSON()
{
return Data?.ToJSON();
}
}
Code language: C# (cs)
C# Generic constraint types and order
C# provides you with five types of generic constraints:
Constraint Type | Description |
---|---|
ClassName | The type argument is the class of this type or classes derived from it. |
class | The type argument can be any reference type including classes, arrays, delegates, and interfaces. |
struct | The type argument can be any value type |
InterfaceName | The type argument can be the interface or types that implement this interface |
new() | The type argument with the parameterless public constructor. |
A generic type can have multiple constraints. In other words, it can have multiple where
clauses like this:
class MyClass<T1, T2, T3>
where T2: MyClass2
where T3: IMyInterface
{
//...
}
Code language: C# (cs)
The order of the where
clauses is not important. However, the constraints in a where
clause matters. The following shows the order of the constraints:
Primary (0 or 1) | Secondary (0 or more) | Constructor (0 or 1) |
---|---|---|
ClassName class struct | InterfaceName | new() |
Summary
- Generic constraints specify constraints on the type used as arguments for type parameters in generic type.
- Use the
where
clauses to specify generic constraints.