Ce face record și imutabilitate
Tipurile record (introduse în C# 9 pentru tipuri referință record class și extinse în C# 10 cu readonly record struct pentru tipuri valoare) sunt concepute special pentru modelarea datelor imutabile: obiecte DTO pentru API-uri REST, evenimente, mesaje din cozi sau rezultate ale interogărilor. O declarație pozițională de o singură linie precum public record Coordonata(double Lat, double Lon); generează automat proprietăți { get; init; }, constructor, deconstructor și metode de comparare.
Spre deosebire de o clasă obișnuită (unde a == b verifică dacă ambele variabile indică aceeași adresă de memorie pe heap), două instanțe de record sunt considerate egale (a == b returnează true și au același GetHashCode()) dacă toate proprietățile lor au valori egale (egalitate bazată pe valoare / structurală). Pentru a actualiza o proprietate fără a modifica obiectul existent, expresia with creează o copie nouă doar cu proprietățile specificate modificate.
Parametri, metode și opțiuni
| Parametru / Metodă | Descriere |
|---|---|
public record Nume(T1 Prop1, T2 Prop2); |
Declară un record pozițional imutabil cu proprietăți init, egalitate pe valoare și ToString() generat automat. |
instanta with { Prop = valNoua } |
Mutație nedistructivă: creează o nouă instanță copiind toate proprietățile existente, cu excepția celor suprascrise în acolade. |
a == b / a.Equals(b) |
Compară valoarea tuturor proprietăților (egalitate structurală), nu referința din memorie. |
var (p1, p2) = instanta; |
Deconstruiește automat un record pozițional într-un tuplu de variabile locale. |
public readonly record struct Nume(...) |
Definește un record ca tip valoare imutabil pe stivă (fără alocare pe heap, C# 10+). |
Exemple
Exemplul 1: Egalitate bazată pe valoare, ToString() automat și expresia with
Observă cum două instanțe diferite cu aceleași date sunt egale la compararea cu == și cum with creează o versiune actualizată fără a altera originalul.
var abonament1 = new Abonament("Pro", 49.90m, UtilizatoriMax: 5);
var abonament2 = new Abonament("Pro", 49.90m, UtilizatoriMax: 5);
Console.WriteLine($"Sunt egale valoric (==): {abonament1 == abonament2}");
Console.WriteLine($"Aceeași referință în memorie: {ReferenceEquals(abonament1, abonament2)}");
// Creăm o variantă cu reducere folosind 'with' (abonament1 rămâne neschimbat!):
var abonamentPromo = abonament1 with { PretLunar = 39.90m };
Console.WriteLine($"Original: {abonament1}");
Console.WriteLine($"Promo: {abonamentPromo}");
public record Abonament(string Plan, decimal PretLunar, int UtilizatoriMax);
Ieșire în consolă
Sunt egale valoric (==): True
Aceeași referință în memorie: False
Original: Abonament { Plan = Pro, PretLunar = 49.90, UtilizatoriMax = 5 }
Promo: Abonament { Plan = Pro, PretLunar = 39.90, UtilizatoriMax = 5 }
Exemplul 2: Deconstrucția unui record și adăugarea de membri calculați
Un record poate avea corp cu acolade în care poți defini proprietăți calculate, metode sau validări suplimentare.
var dreptunghi = new Dreptunghi(Latime: 12.5, Inaltime: 4.0);
// Deconstrucție pozițională automată:
var (lat, inalt) = dreptunghi;
Console.WriteLine($"Dimensiuni: {lat} x {inalt} -> Arie: {dreptunghi.Arie}, Perimetru: {dreptunghi.Perimetru}");
public record Dreptunghi(double Latime, double Inaltime)
{
public double Arie => Latime * Inaltime;
public double Perimetru => 2 * (Latime + Inaltime);
}
Ieșire în consolă
Dimensiuni: 12.5 x 4 -> Arie: 50, Perimetru: 33
Greșeli frecvente
- Includerea unui tablou
T[]sau a uneiList<T>într-unrecordașteptând caa == bsă compare conținutul listelor element cu element — egalitatea generată automat de compilator apeleazăEqualspe fiecare proprietate, iar pentruList<T>șiT[]metodaEqualscompară doar referința listei! - Folosirea unui
recordpentru entități EF Core urmărite de Change Tracker — entitățile EF Core se bazează pe identitatea referinței și mutabilitatea proprietăților, deci pentru ele este recomandatclassobișnuit, în timp cerecordeste ideal pentru DTO-uri și Value Objects.
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.switch expression și Pattern Matching: Evaluează expresii concise bazate pe forma, tipul, proprietățile sau intervalele valorilor folosind pattern matching modern.JsonSerializer (System.Text.Json): Serializează obiecte C# în format JSON și deserializează text JSON înapoi în tipuri puternic tipizate, cu performanță nativă ridicată.