CSharp - Generics Generic Constraints

Introduction

Normally you can use any type to replace the generic type.

You can add constraints to a type parameter to require more specific type arguments.

These are the possible constraints:

Syntax Meaning
where T : base-classBase-class constraint
where T : interface Interface constraint
where T : class Reference-type constraint
where T : structValue-type constraint (excludes Nullable types)
where T : new() Parameterless constructor constraint
where U : T Naked type constraint

In the following code, GenericClass<T,U> requires T to derive from SomeClass (or itself) and implement Interface1, and requires U to provide a parameterless constructor:

class SomeClass {
}
interface Interface1 {
}

class GenericClass<T,U> where T : SomeClass, Interface1
                        where U : new(){
}

Constraints can be applied in both methods and type definitions.

base-class/interface constraint

A base-class constraint sets that the type parameter must subclass or match a particular class.

An interface constraint sets that the type parameter must implement that interface.

The following code creates a generic Max method to return the maximum of two values.

We can use the generic interface from C# called IComparable<T>:

interface IComparable<T>{
   int CompareTo (T other);
   ...
}

Using this interface as a constraint, we can write a Max method as follows:

static T Max <T> (T a, T b) where T : IComparable<T>
{
       return a.CompareTo (b) > 0 ? a : b;
}

Max method can accept arguments of any type implementing IComparable<T>.

int z = Max (5, 10);               // 10
string last = Max ("ant", "zoo");  // zoo

class/struct constraint

class constraint and struct constraint set that T must be a reference type.

The parameterless constructor constraint requires T to have a public parameterless constructor.

Then you can call new() on T:

static void Initialize<T> (T[] array) where T : new()
{
    for (int i = 0; i < array.Length; i++)
         array[i] = new T();
}

Naked type constraint

The naked type constraint requires one type parameter to derive from or match another type parameter.

FilteredTable returns another Table, containing only the subset of elements where the type parameter U is of the type parameter T:

class Table<T>
{
    Table<U> FilteredTable<U>() where U : T {...}
}

Related Topics