C# Generic Constraints

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 TypeDescription
ClassNameThe type argument is the class of this type or classes derived from it.
classThe type argument can be any reference type including classes, arrays, delegates, and interfaces.
structThe type argument can be any value type
InterfaceNameThe 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
InterfaceNamenew()

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.
Was this tutorial helpful ?