Generics power all collections and most libraries, and let you avoid casts and boxing.
Generics let a class or method work with any type while staying type-safe: List<T>, Dictionary<TKey, TValue>, Task<T>. You write the code once with a type parameter and the compiler checks every use.
Generic method: static T Max<T>(T a, T b) where T : IComparable<T> => a.CompareTo(b) >= 0 ? a : b;. The type argument is usually inferred: Max(3, 7).
Generic class: class Box<T> { public T Value { get; set; } }, used as new Box<string>().
Constraints (where clauses) say what T must support, so you can call those members:
where T : class/where T : struct(reference or value type),where T : notnull.where T : new()requires a parameterless constructor.where T : SomeBaseorwhere T : IComparable<T>for a base class or interface.where T : INumber<T>(generic math, .NET 7+) lets you use+andT.Zeroon any numeric type.
Unlike Java, .NET generics are reified: the runtime knows List<int> from List<string>, typeof(T) works, and value-type instantiations get specialized code without boxing.
default(T) (or just default) gives null for reference types and zero for value types. Prefer generics over object parameters: you keep compile-time checks and avoid casts.
Example
using System.Numerics;
static T Max<T>(T a, T b) where T : IComparable<T>
=> a.CompareTo(b) >= 0 ? a : b;
static T Sum<T>(IEnumerable<T> xs) where T : INumber<T>
{
T total = T.Zero;
foreach (var x in xs) total += x;
return total;
}
Console.WriteLine(Max(3, 7)); // 7
Console.WriteLine(Max("pear", "apple")); // pear
Console.WriteLine(Sum([1.5, 2.5])); // 4
Console.WriteLine(Sum([1, 2, 3])); // 6
var box = new Box<int>(42);
Console.WriteLine(box); // Box<Int32>(42)
class Box<T>(T value)
{
public T Value { get; } = value;
public override string ToString() => $"Box<{typeof(T).Name}>({Value})";
}Remember
- Type parameters give reuse without casts or boxing
- Constraints (where T : ...) unlock members you can call on T
- Type arguments are usually inferred for generic methods
- .NET generics are reified: typeof(T) works at runtime