Generics, Constraints, Covariance & Contravariance Masterclass

โšก C# 12 & .NET 8 ๐ŸŸข Chapter 20 of 35 ๐Ÿ“‚ Phase 7: Advanced C# Language ๐Ÿ“… 2026 Edition
๐Ÿ“Œ Covered in this chapter: Generic Types ยท Generic Methods ยท Generic Classes ยท Type Parameters ยท Constraints (where) ยท Generic Interfaces ยท Covariance (out) ยท Contravariance (in)

Welcome to Phase 7 (Chapter 20): C# Generics, Constraints, Covariance & Contravariance Masterclass! Generics allow you to define classes, methods, interfaces, and delegates with placeholder type parameters (<T>). In this lesson, we master type-safe reusable code, generic constraints (where T : class, new(), IComparable), multiple type parameters, covariance (out), and contravariance (in).

1Generic Methods & Generic Classes

Without generics, creating reusable components required using object, which causes runtime type cast errors and boxing performance hits. Generics enforce **compile-time type safety**.

C# โ€” Generic Method & Class Example โ–ถ Run in Compiler
// Generic Method
static T GetFirst<T>(List<T> items)
{
    if (items == null || items.Count == 0)
        throw new InvalidOperationException("List is empty!");
    return items[0];
}

List<int> numbers = new() { 10, 20, 30 };
Console.WriteLine($"First number: {GetFirst(numbers)}"); // Output: 10

List<string> names = new() { "Ravi", "Alice", "Bob" };
Console.WriteLine($"First name: {GetFirst(names)}");   // Output: Ravi

// Generic Class
public class DataRepository<T>
{
    private List<T> _storage = new();

    public void Add(T item) => _storage.Add(item);
    public T Get(int index) => _storage[index];
    public int Count => _storage.Count;
}

DataRepository<double> doubleRepo = new();
doubleRepo.Add(99.95);
Console.WriteLine($"Repo item: {doubleRepo.Get(0)}");
2Generic Constraints (where T : ...)

Generic constraints restrict the types that can be passed as arguments for type parameter T, allowing access to methods defined on those constrained types:

Constraint SyntaxRequirement for Type Argument T
where T : structMust be a non-nullable Value Type (int, double, struct).
where T : classMust be a Reference Type (class, interface, delegate, string).
where T : new()Must have a public parameterless constructor.
where T : BaseClassMust derive from specified BaseClass.
where T : ISomeInterfaceMust implement specified Interface.
C# โ€” Generic Constraints Example โ–ถ Run in Compiler
public class Repository<TEntity> where TEntity : class, new()
{
    public TEntity CreateInstance()
    {
        return new TEntity(); // Valid because of new() constraint!
    }
}
3Technical FAQs

Q1: What is Covariance (out) and Contravariance (in)?

Covariance (IEnumerable<out T>) allows you to use a more derived type than originally specified. Contravariance (Action<in T>) allows you to use a more generic base type.