Class, Properties { get; init; } și Primary Constructors în C# (.NET)

OOP modern și programare asincronă C# 3.0+ (init în C# 9+, required în C# 11+, Primary Constructors în C# 12+)

Definește clase moderne cu proprietăți auto-implementate, inițializare sigură (init, required) și constructori primari conciși.

Sintaxă C#
public class Serviciu(ILogger logger) { public required string Nume { get; init; } }

Ce face Class, Properties { get; init; } și Primary Constructors

În C#, clasele (class) încapsulează starea și comportamentul prin proprietăți (Properties) în locul câmpurilor publice și al metodelor manuale getX() / setX(). O proprietate auto-implementată precum public decimal Sold { get; private set; } generează automat în fundal un câmp privat și permite citirea publică, dar restricționează modificarea doar în interiorul clasei.

Versiunile recente de C# au eliminat aproape complet codul repetitiv (boilerplate): accesorul init (C# 9) permite setarea unei proprietăți doar la crearea obiectului (în constructor sau în object initializer new Cont { Iban = "..." }), după care proprietatea devine imutabilă; modificatorul required (C# 11) îl obligă pe apelant să furnizeze o valoare pentru acea proprietate la inițializare; iar Primary Constructors pentru clase (C# 12) permit declararea parametrilor de constructor (de exemplu dependențele pentru Dependency Injection) direct în antetul clasei.

Parametri, metode și opțiuni

Parametru / Metodă Descriere
{ get; set; } Proprietate auto-implementată citibilă și modificabilă oricând după crearea obiectului.
{ get; private set; } Proprietate citibilă din exterior, dar modificabilă exclusiv prin metodele interne ale clasei.
{ get; init; } Proprietate care poate fi setată doar în timpul construcției / inițializării obiectului (C# 9+), devenind apoi read-only.
required public T Prop { get; init; } Forțează la compilare inițializarea proprietății în blocul new Clasa { Prop = ... } (C# 11+).
public class Nume(Tip param) Constructor primar (C# 12+): parametrul param este disponibil în toate metodele și proprietățile clasei.
public Tip Prop => expresie; Proprietate calculată (expression-bodied property, doar cu get) evaluată la fiecare citire.

Exemple

Exemplul 1: Clasă cu required, { get; init; } și încapsulare cu { get; private set; }

Proprietățile de identitate (Iban, Titular) sunt marcate cu required și init (nu mai pot fi schimbate ulterior), iar Sold poate fi modificat doar prin metodele validate Depune și Retrage.

var cont = new ContBancar
{
    Iban = "RO49AAAA1B31007593840000",
    Titular = "Andrei Ionescu"
};

cont.Depune(1500m);
cont.Retrage(350m);
Console.WriteLine($"{cont.Titular} ({cont.Iban}) -> Sold: {cont.Sold:F2} RON (Activ: {cont.EsteActiv})");

public class ContBancar
{
    public required string Iban { get; init; }
    public required string Titular { get; init; }
    public decimal Sold { get; private set; }
    public bool EsteActiv => Sold > 0m;

    public void Depune(decimal suma)
    {
        if (suma <= 0) throw new ArgumentOutOfRangeException(nameof(suma));
        Sold += suma;
    }

    public void Retrage(decimal suma)
    {
        if (suma <= 0 || suma > Sold) throw new InvalidOperationException("Fonduri insuficiente.");
        Sold -= suma;
    }
}

Ieșire în consolă

Andrei Ionescu (RO49AAAA1B31007593840000) -> Sold: 1150.00 RON (Activ: True)

Exemplul 2: Constructor primar (Primary Constructor) în C# 12 pentru servicii

În C# 12, nu mai este nevoie să declari un câmp privat private readonly decimal _cotaTva; și un constructor separat: parametrii din antetul clasei sunt capturați automat.

var calculator = new CalculatorPret(cotaTva: 0.19m, moneda: "RON");
Console.WriteLine(calculator.FormateazaPretCuTva(100m));
Console.WriteLine(calculator.FormateazaPretCuTva(250m));

public class CalculatorPret(decimal cotaTva, string moneda)
{
    public decimal CalculeazaBrut(decimal pretNet) => Math.Round(pretNet * (1 + cotaTva), 2);

    public string FormateazaPretCuTva(decimal pretNet) =>
        $"Net: {pretNet:F2} {moneda} -> Cu TVA ({cotaTva:P0}): {CalculeazaBrut(pretNet):F2} {moneda}";
}

Ieșire în consolă

Net: 100.00 RON -> Cu TVA (19%): 119.00 RON
Net: 250.00 RON -> Cu TVA (19%): 297.50 RON

Greșeli frecvente

  • Expunerea unor câmpuri publice (public string Nume;) în loc de proprietăți (public string Nume { get; set; }) — bibliotecile de serializare, validare și ORM-urile (precum Entity Framework Core sau ASP.NET Core Model Binding) se bazează pe proprietăți.
  • Lăsarea tuturor proprietăților cu { get; set; } public atunci când obiectul nu ar trebui modificat după creare; preferă { get; init; } sau { get; private set; } pentru a preveni mutațiile accidentale.

Funcții și concepte înrudite

  • 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).
  • interface și tipuri generice <T>: Definește contracte decuplate prin interfețe și scrie clase sau metode reutilizabile și sigure la compilare prin parametri generici <T>.
  • JsonSerializer (System.Text.Json): Serializează obiecte C# în format JSON și deserializează text JSON înapoi în tipuri puternic tipizate, cu performanță nativă ridicată.