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:
- class or struct
- Base class
- Interfaces
- 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();
}