# Generics and Constraints — C#/.NET

Source: https://www.skillbyai.com/en/dotnet/t-generics

> Write type-safe reusable code with generic types, methods and constraints.

## One implementation, many types

**Generics** let you write a type or method once with a **type parameter**, such as `List<T>` or `Dictionary<TKey, TValue>`, and use it with any type while keeping full compile-time type checking and avoiding casts. Unlike Java's type erasure, .NET generics are **reified**: the runtime knows `List<int>` and `List<string>` are different types, and generic code over value types is specialised, so `List<int>` stores ints without boxing. **Constraints** with `where` restrict what `T` can be, and in return let you use its members: `where T : class`, `where T : struct`, `where T : notnull`, `where T : new()` (has a parameterless constructor), `where T : SomeBase`, `where T : IComparable<T>`. Generic math, through interfaces such as `INumber<T>`, lets one method work for `int`, `double` and `decimal`. **Variance** (`out T` in `IEnumerable<out T>`) allows an `IEnumerable<string>` to be used as an `IEnumerable<object>`.

## A template with a slot for a type

A generic type is a mould with a type-shaped slot; each use fills the slot with a concrete type.

![A mould shape with an empty slot, and three filled copies beside it, each with a differently shaped piece in the slot.](assets/figures/dotnet/section-4-map.svg) — Figure 4.1 — One generic definition, several constructed types.

## Generic methods with constraints

The constraint makes `CompareTo` and arithmetic available on T.

```cs
using System.Numerics;

T MaxOf<T>(IEnumerable<T> items) where T : IComparable<T>
{
    using var e = items.GetEnumerator();
    if (!e.MoveNext()) throw new ArgumentException("Empty sequence", nameof(items));
    T max = e.Current;
    while (e.MoveNext())
        if (e.Current.CompareTo(max) > 0) max = e.Current;
    return max;
}

T SumOf<T>(IEnumerable<T> items) where T : INumber<T>
{
    T total = T.Zero;
    foreach (var x in items) total += x;
    return total;
}

Console.WriteLine(MaxOf(new[] { "pear", "apple", "mango" }));
Console.WriteLine(SumOf(new[] { 1.5m, 2.25m }));
```

## Avoid object and casting

Code that stores everything as `object` and casts back loses type safety and boxes value types. If you find yourself casting, a generic type parameter is usually the better design.

**Quiz:** How do .NET generics differ from Java generics?

- [ ] .NET generics are erased at run time
- [ ] .NET has no generic constraints
- [x] .NET generics are reified, so the runtime knows the type arguments and value types avoid boxing
- [ ] .NET generics only work with classes

*Answer:* .NET generics are reified, so the runtime knows the type arguments and value types avoid boxing. .NET keeps generic type information at run time and specialises code for value types.
