What are generics in C#?
Quick Answer
Generics let you write a class, method, interface or delegate with a placeholder type parameter (T) that the caller fills in. The same code works for any type, with full compile-time type safety and no boxing — List<int> and List<string> share one implementation but are strongly typed.
Generics let me write code once with a type placeholder — T — and the caller decides the actual type. So List<T> is one implementation but List<int> and List<Customer> are both fully type-checked at compile time, and value types aren't boxed. That's basically why they were added — the old non-generic collections stored object, so you got casts and boxing everywhere. I can also add constraints, like where T : IComparable<T>, so I can actually call methods on T inside the generic.
Before generics you either wrote a type-specific class for every type, or used object and paid for casts and boxing (the old ArrayList). Generics give you one implementation that the compiler specialises.
- Type safety —
List<int>won't accept a string; the error is at compile time. - Performance — value types are stored directly, no boxing; the JIT shares one native implementation for all reference-type arguments and a specialised one per value type.
- Constraints restrict
T:where T : class,where T : struct,where T : new(),where T : IComparable<T>,where T : BaseClass. Constraints let you call members onTinside the generic. - Generic methods can infer
Tfrom arguments:Swap(ref a, ref b).
Generics power the whole collection library, LINQ, Task<T>, Nullable<T> and dependency injection (ILogger<T>).
public class Repository<T> where T : class, IEntity
{
private readonly List<T> _items = new();
public T? Find(int id) => _items.FirstOrDefault(x => x.Id == id);
public void Add(T item) => _items.Add(item);
}
// generic method with type inference
static T Max<T>(T a, T b) where T : IComparable<T>
=> a.CompareTo(b) >= 0 ? a : b;
- One implementation, many types — no copy-paste type-specific classes
- Compile-time type safety instead of runtime cast errors
- No boxing for value types in generic collections
- Constraints let the generic code use members of T safely