Generics let you write code that works with any type while remaining type-safe — no casting, no boxing, no runtime errors from wrong types.
// Without generics — duplicated for every type
static int Max(int a, int b) => a > b ? a : b;
static double Max(double a, double b) => a > b ? a : b;
// With generics — one method for all comparable types
static T Max<T>(T a, T b) where T : IComparable<T> =>
a.CompareTo(b) > 0 ? a : b;
Console.WriteLine(Max(3, 7)); // 7
Console.WriteLine(Max(3.14, 2.71)); // 3.14
Console.WriteLine(Max("apple", "banana")); // banana
public class Box<T>
{
private T _value;
public Box(T value) => _value = value;
public T Get() => _value;
public void Set(T value) => _value = value;
public override string ToString() => $"Box<{typeof(T).Name}>: {_value}";
}
var intBox = new Box<int>(42);
var strBox = new Box<string>("hello");
Console.WriteLine(intBox); // Box<Int32>: 42
Console.WriteLine(strBox); // Box<String>: hello
// where T : class — reference type
// where T : struct — value type
// where T : new() — has parameterless constructor
// where T : SomeClass — derives from SomeClass
// where T : IInterface — implements interface
public class Repository<T> where T : class, new()
{
private List<T> _store = new();
public void Add(T item) => _store.Add(item);
public T? Get(int i) => i < _store.Count ? _store[i] : null;
public int Count => _store.Count;
}