Constrângeri generice (where T : …) în C# (.NET)

Colecții generice C# 2.0 (notnull, unmanaged: C# 7.3/8.0)

Clauza where limitează tipurile acceptate de un generic (class, struct, new(), o interfață sau clasă de bază) ca să le poți folosi membrii.

Sintaxă C#
T Max<T>(T a, T b) where T : IComparable<T>   class Repo<T> where T : class, IEntitate, new()

Ce face Constrângeri generice (where T : ...)

Fără constrângeri, în interiorul unei metode generice poți folosi doar ce are orice object. Clauza where T : ... promite compilatorului ce știe tipul și îți dă acces la membrii respectivi: cu where T : IComparable<T> poți apela CompareTo, cu where T : new() poți scrie new T().

Constrângerile uzuale sunt: class (tip referință), struct (tip valoare nenul), notnull, unmanaged, new() (constructor public fără parametri, pus ultimul), o clasă de bază sau una ori mai multe interfețe. Din C# 13 există și allows ref struct.

Fiecare parametru de tip are propria clauză where. Un argument care nu respectă constrângerea dă eroare la compilare, nu la rulare.

Parametri

Parametru Descriere
where T : class / struct Tip referință, respectiv tip valoare.
where T : IInterfata T trebuie să implementeze interfața; îi poți apela membrii.
where T : Baza T este Baza sau o clasă derivată.
where T : new() T are constructor public fără parametri.

Exemple

Exemplul 1: Maxim generic cu IComparable

Console.WriteLine(Maxim(3, 9));
Console.WriteLine(Maxim("Cluj", "Arad"));
Console.WriteLine(Maxim(new DateOnly(2026, 1, 5), new DateOnly(2025, 12, 31)).Year);

static T Maxim<T>(T a, T b) where T : IComparable<T> =>
    a.CompareTo(b) >= 0 ? a : b;

Rezultat

9
Cluj
2026

Exemplul 2: Depozit generic cu interfață și new()

var repo = new Depozit<Produs>();
repo.Adauga(new Produs { Id = 1, Nume = "Laptop" });
var gol = repo.CreeazaGol();

Console.WriteLine(repo.Cauta(1)?.Nume);
Console.WriteLine(repo.Cauta(2)?.Nume ?? "nu există");
Console.WriteLine(gol.Id);

interface IEntitate { int Id { get; } }

class Produs : IEntitate
{
    public int Id { get; set; }
    public string Nume { get; set; } = "";
}

class Depozit<T> where T : class, IEntitate, new()
{
    private readonly List<T> _elemente = [];
    public void Adauga(T e) => _elemente.Add(e);
    public T? Cauta(int id) => _elemente.FirstOrDefault(e => e.Id == id);
    public T CreeazaGol() => new T();
}

Rezultat

Laptop
nu există
0

Greșeli frecvente

  • new() trebuie să fie ultima constrângere, iar class/struct prima.
  • Operatorii <, + sau == nu merg pe un T oarecare. Pentru comparații folosește IComparable<T>; pentru aritmetică, interfețele de generic math (INumber<T>).
  • Cu tipurile nullable activate, where T : class cere un tip referință fără ? (altfel primești avertisment), iar where T : class? acceptă și string?.

Comenzi înrudite

  • Metode de extensie (this): Adaugă metode unui tip existent (string, int, interfețe) fără să-l modifici: metodă statică cu this în fața primului parametru.
  • IEnumerable<T> și yield return: IEnumerable<T> este orice secvență care se poate parcurge cu foreach; yield return produce elementele pe rând, doar când sunt cerute.