Generic Constraints in C#
Last Updated : 22 Sep, 2025
In C#, Generics allow classes, methods, interfaces and delegates to work with any data type. However, sometimes you may want to restrict the type of arguments that can be passed to a generic type. This is where constraints come in.
Generic constraints specify requirements for the type parameters in generics, ensuring only suitable types are used.
Syntax
class ClassName<T> where T : constraint{
// members using T
}
- T: Type parameter.
- constraint: Rule applied on T.
Why Use Constraints
Without constraints, a generic type can accept any type. Constraints help in:
- Restricting types: Limit type parameters to reference types, value types or specific classes/interfaces.
- Enabling functionality: Allow use of certain members or constructors of the type parameter.
- Improving type safety: Prevent invalid type substitutions at compile-time.
Types of Constraints in C#
1. where T : struct
- Restricts T to value types (like int, double, bool, or user-defined struct).
- Cannot be null.
- Cannot be used with nullable value types (int?, double?).
C# class ValueContainer<T> where T : struct { public T Data { get; set; } } Use this when you want to ensure the generic type is always a non-nullable value type.
2. where T : class
- Restricts T to reference types (class, interface, delegate, array).
- Allows null values.
C# class ReferenceContainer<T> where T : class { public T Data { get; set; } } Useful when designing classes that should work only with objects and not primitive/value types.
3. where T : new()
- Restricts T to types that have a public parameterless constructor.
- Must appear last when combining multiple constraints.
C# class Factory<T> where T : new() { public T CreateInstance() { return new T(); // Safe to create instance } } Helpful when you want to instantiate objects of type T inside the generic class.
4. where T : BaseClassName
- Restricts T to types that inherit from a specific base class.
- Allows access to members of the base class inside the generic class.
C# class Animal { } class Dog : Animal { } class AnimalContainer<T> where T : Animal { public T Data { get; set; } } Ensures only classes derived from Animal (like Dog) can be used as type arguments.
5. where T : InterfaceName
- Restricts T to types that implement a specific interface.
- Enables calling interface methods on the generic parameter.
C# interface IShape { void Draw(); } class ShapeContainer<T> where T : IShape { public void Render(T shape) { shape.Draw(); } } Ensures all type arguments follow a specific contract.
6. Multiple Constraints
- More than one constraint can be applied using a comma.
- Example: Restrict T to be a reference type and must have a parameterless constructor.
C++ class Sample<T> where T : class, new() { public T Create() { return new T(); } } Common in factory patterns where you want reference types that can be instantiated.
7. Constraints on Multiple Type Parameters
Each type parameter can have its own constraint.
C# class Sample<T1, T2> where T1 : class where T2 : struct { public T1 RefData { get; set; } public T2 ValData { get; set; } } Ensures fine-grained control when working with multiple generics.
Example: Using Constraints Together
C# using System; interface ILogger { void Log(string message); } class ConsoleLogger : ILogger { public void Log(string message) => Console.WriteLine(message); } class Service<T> where T : ILogger, new() { public void Execute() { T logger = new T(); logger.Log("Service executed successfully!"); } } class Program { static void Main() { Service<ConsoleLogger> service = new Service<ConsoleLogger>(); service.Execute(); } } OutputService executed successfully!
Note
- Constraints prevent misuse of generics by restricting type arguments.
- They make generic code more predictable and reliable.
- Many built-in generic collections and LINQ methods internally use constraints for safety.
Explore
Introduction
Fundamentals
Control Statements
OOP Concepts
Methods
Arrays
ArrayList
String
Tuple
Indexers