Skip to main content

Basic Syntax

Declaration​

Generic Class​

public class Box<T>
{
public T Value { get; set; }
}

Generic Method​

public T Method<T>(T value)
{
return value;
}

Generic Interface​

public interface IRepository<T>
{
T Get(int id);
}

Generic Constraints​

public class Factory<T> where T : new()
{
public T Create() => new T();
}
tip
  • T is a type parameter (a placeholder)
  • where T : new() is a constraint, which restricts what kinds of types can be used

Constraint​

Generic constraints specify the requirements that a type parameter must satisfy. They allow the compiler to ensure that T supports certain operations, such as having a parameterless constructor, inheriting from a specific base class, or implementing an interface.

Without constraints, the compiler cannot assume anything about T.

Basic Syntax​

class MyClass<T> where T : ConstraintType
{
}

Multiple constraints can be combined:

class MyClass<T> where T : BaseClass, IInterface, new()
{
}

Constraint order must follow this pattern:

  1. class or struct
  2. Base class
  3. Interfaces
  4. new()

Common Constraint Types​

Reference type constraint​

where T : class

Value type constraint​

where T : struct

Parameterless constructor​

where T : new()

Inherit from a base class​

where T : BaseClass

Implement an interface​

where T : IDisposable

Why Constraints Are Needed​

Calling members that may not exist​

void CloseItem<T>(T item)
{
item.Dispose(); // Cannot compile
}
void CloseItem<T>(T item) where T : IDisposable
{
item.Dispose(); // ✔ Safe
}

Creating instances of T​

class Factory<T>
{
T Create() => new T(); // Not allowed
}
class Factory<T> where T : new()
{
T Create() => new T(); // ✔ Allowed
}

Method-Level Constraints​

Constraints can also be applied to individual methods:

public void Save<T>(T item) where T : IEntity
{
item.Store();
}