C#

Generics

6 question(s)

What are generics and why use them?

Beginner
Generics let you write type-parameterised classes/methods that work with any type while keeping compile-time type safety and avoiding boxing.
public class Box<T> { public T Value { get; set; } }
var b = new Box<int> { Value = 5 };
Real-world example List<T> and Dictionary<K,V> give type-safe collections without casts.

What are generic constraints and why are they needed?

Intermediate
Constraints (where T : ...) restrict the type argument so you can use its members — e.g. class, struct, new(), a base type, or an interface.
T Create<T>() where T : new() => new T();
void Sort<T>(List<T> x) where T : IComparable<T> {}
Real-world example Constraining a repository's entity to a base Entity type so you can read its Id.

What is covariance and contravariance in generics?

Intermediate
out (covariant) lets you use a more-derived type where a less-derived is expected (IEnumerable<out T>); in (contravariant) lets a less-derived be used where more-derived is expected (IComparer<in T>).
IEnumerable<object> objs = new List<string>(); // covariance
Real-world example Returning IEnumerable<Animal> from a method that actually yields Dogs.

How are generics implemented for value types vs reference types in the CLR?

Advanced
The JIT creates a specialised instantiation per value type (no boxing, better perf) but shares a single instantiation for all reference types, since they are the same size (a pointer).
List<int>   // dedicated code, no boxing
List<string>, List<object> // share one instantiation
Real-world example This is why List<int> outperforms ArrayList — no boxing per element.

What problem do generic math / static abstract interface members solve?

Advanced
They let you write algorithms over any numeric type by constraining to interfaces like INumber<T> with static abstract operators, removing duplicated overloads.
T Sum<T>(IEnumerable<T> xs) where T : INumber<T> {
    T total = T.Zero; foreach (var x in xs) total += x; return total; }
Real-world example One Sum method that works for int, double and decimal alike.

Why can't you use operators like + directly on an unconstrained generic T?

Intermediate
Because the compiler doesn't know T supports the operator. You must constrain T (e.g. to INumber<T>) or pass a delegate/strategy that performs the operation.
// error without constraint: T r = a + b;
T Add<T>(T a, T b) where T : INumber<T> => a + b;
Real-world example Generic aggregation helpers require a numeric constraint to add values.