Ce face interface și tipuri generice <T>
O interfață (interface) definește un contract de metode și proprietăți pe care orice clasă sau structură îl poate implementa. În arhitectura aplicațiilor moderne .NET (ASP.NET Core), interfețele stau la baza Dependency Injection (DI) și a testării unitare: un controller sau serviciu depinde de o abstracție precum IEmailSender sau IOrderRepository, nu de implementarea concretă.
Tipurile și metodele generice (<T>) îți permit să scrii algoritmi, containere (precum List<T>, Result<T>, PagedList<T>) și interfețe (IRepository<T>) fără a duplica codul pentru fiecare tip de date și fără a pierde siguranța tipurilor prin conversii la object (boxing / unboxing). Prin clauza where T : ... poți impune constrângeri asupra parametrului generic, astfel încât compilatorul să garanteze că T implementează o anumită interfață, este o clasă (class) sau are un constructor fără parametri (new()).
Parametri, metode și opțiuni
| Parametru / Metodă | Descriere |
|---|---|
public interface INume { ... } |
Definește un contract; prin convenție în .NET, numele interfețelor încep întotdeauna cu litera I. |
class Serviciu : IInterfata1, IInterfata2 |
O clasă poate moșteni cel mult o clasă de bază, dar poate implementa oricâte interfețe. |
Clasa<T> / Metoda<T>(T param) |
Introduce un parametru de tip generic rezolvat la compilare fără costuri de boxing. |
where T : class / struct / notnull |
Constrânge T să fie un tip referință, un tip valoare non-nullable sau orice tip non-null. |
where T : IComparable<T>, new() |
Obligă tipul T să implementeze interfața specificată și să expună un constructor public fără parametri. |
Exemple
Exemplul 1: Tip generic Rezultat<T> și interfață decuplată pentru procesarea plăților
Definim o interfață IProcesatorPlata și o înregistrare generică Rezultat<T> care încapsulează elegant succesul sau eroarea unei operațiuni.
IProcesatorPlata procesator = new ProcesatorCardStripe();
Rezultat<string> raspuns = procesator.Incaseaza(250.00m, "RON");
if (raspuns.Succes)
{
Console.WriteLine($"Plată reușită! ID tranzacție: {raspuns.Valoare}");
}
public record Rezultat<T>(bool Succes, T? Valoare, string? Eroare)
{
public static Rezultat<T> Ok(T valoare) => new(true, valoare, null);
public static Rezultat<T> Esec(string eroare) => new(false, default, eroare);
}
public interface IProcesatorPlata
{
Rezultat<string> Incaseaza(decimal suma, string moneda);
}
public class ProcesatorCardStripe : IProcesatorPlata
{
public Rezultat<string> Incaseaza(decimal suma, string moneda) =>
suma > 0
? Rezultat<string>.Ok($"ch_stripe_{suma:0}_{moneda}")
: Rezultat<string>.Esec("Suma trebuie să fie pozitivă.");
}
Ieșire în consolă
Plată reușită! ID tranzacție: ch_stripe_250_RON
Exemplul 2: Metodă generică cu constrângere where T : IEntity
Datorită constrângerii where T : IEntitate, metoda generică GasesteDupaId poate accesa proprietatea .Id pe orice tip T care implementează interfața.
var clienti = new List<Client> { new(10, "SC Alfa SRL"), new(20, "SC Beta SA") };
Client? gasit = GasesteDupaId(clienti, 20);
Console.WriteLine($"Entitate găsită: {gasit?.Denumire}");
static T? GasesteDupaId<T>(IEnumerable<T> colectie, int idCautat) where T : IEntitate
=> colectie.FirstOrDefault(x => x.Id == idCautat);
public interface IEntitate
{
int Id { get; }
}
public record Client(int Id, string Denumire) : IEntitate;
Ieșire în consolă
Entitate găsită: SC Beta SA
Greșeli frecvente
- Folosirea tipului
objectîn locul unui parametru generic<T>— stocarea unui tip valoare (int,decimal,struct) într-unobjectprovoacă o alocare pe heap (boxing) și necesită conversie explicită (cast) nesigură la citire. - Uitarea constrângerii
where T : classsauwhere T : IInterfataatunci când încerci să accesezi metode specifice pe o variabilă de tip genericTîn interiorul metodei.
Funcții și concepte înrudite
Class, Properties { get; init; } și Primary Constructors: Definește clase moderne cu proprietăți auto-implementate, inițializare sigură (init, required) și constructori primari conciși.record și imutabilitate: Definește într-o singură linie modele de date imutabile cu egalitate bazată pe valoare, afișare automată și clonare nedistructivă (with).List<T>: Stochează o listă generică redimensionabilă dinamic, cu acces rapid după index și metode de adăugare, ștergere și căutare.async / await și Task<T>: Execută operațiuni I/O neblocante (rețea, baze de date, fișiere) și coordonează sarcini concurente cu Task și Task.WhenAll.