CSharp/C# Tutorial - C# Generic Constraints






By default, a type parameter can be substituted with any type whatsoever.

Constraints can be applied to a type parameter to require more specific type arguments.

These are the possible constraints:

where T : base-class // Base-class constraint 
where T : interface  // Interface constraint 
where T : class      // Reference-type constraint 
where T : struct     // Value-type constraint (excludes Nullable types) 
where T : new()      // Parameterless constructor constraint 
where U : T          // Naked type constraint 




Example

In the following example, GenericClass<T,U> requires T to derive from Main and implement Interface1, and requires U to provide a parameterless constructor:

class Main {} 
interface Interface1 {} 

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




Note

Constraints can be applied wherever type parameters are defined, in both methods and type definitions.

A base-class constraint specifies that the type parameter must subclass a particular class.

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

The following code shows how to write a generic Max method, which returns the maximum of two values.

We can use the generic interface defined in the framework called IComparable<T>:

public interface IComparable<T> // Simplified version of interface 
{
    int CompareTo (T other); 
} 

The following code uses IComparable 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; 
} 

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

int z = Max (5, 10); // 10 
string last = Max ("A", "B");

Subclassing Generic Types

A generic class can be subclassed like a nongeneric class.

The subclass can leave the base class's type parameters open, as in the following example:

class Stack<T> {...} 
class SpecialStack<T> : Stack<T> {...} 

Or the subclass can close the generic type parameters with a concrete type:

class IntStack : Stack<int> {...} 

A subtype can also introduce fresh type arguments:

class List<T> {...} 
class KeyedList<T,TKey> : List<T> {...}