ref, out și in în C# (.NET)

Bazele limbajului și I/O C# 1.0 (in: C# 7.2)

Transmit un argument prin referință: ref îl poate citi și modifica, out trebuie setat de metodă, in este doar pentru citire.

Sintaxă C#
void M(ref int a, out int b, in Punct c)  // apel: M(ref x, out var y, in p)

Ce face ref, out și in

Implicit, C# transmite argumentele prin valoare: metoda primește o copie, iar schimbarea ei nu afectează variabila apelantului. Cu ref, metoda lucrează direct pe variabila apelantului, care trebuie să fie inițializată înainte de apel.

out este pentru valori de ieșire: variabila nu trebuie inițializată, dar metoda este obligată să-i dea o valoare înainte să se încheie. E modelul folosit de int.TryParse și Dictionary.TryGetValue. Variabila se poate declara direct la apel: out var rezultat.

in transmite prin referință doar pentru citire, ca să eviți copierea unor struct-uri mari. Pentru un singur rezultat suplimentar, de multe ori un tuple returnat e mai simplu decât out.

Parametri

Parametru Descriere
ref Citire și scriere; argumentul trebuie inițializat. La apel se scrie ref.
out Doar ieșire; metoda trebuie să atribuie valoarea. La apel se scrie out.
in Doar citire, fără copiere. La apel in e opțional.

Exemple

Exemplul 1: Valoare vs. ref

int sold = 100;

AdaugaCopie(sold, 50);
Console.WriteLine(sold);

AdaugaRef(ref sold, 50);
Console.WriteLine(sold);

static void AdaugaCopie(int valoare, int suma) => valoare += suma;
static void AdaugaRef(ref int valoare, int suma) => valoare += suma;

Rezultat

100
150

Exemplul 2: out pentru o metodă de tip Try

Metoda returnează bool (a reușit sau nu), iar valoarea propriu-zisă vine prin out.

string[] intrari = ["Iași:700000", "Cluj", "Arad:fără"];

foreach (string text in intrari)
{
    if (IncearcaCitire(text, out string oras, out int locuitori))
        Console.WriteLine($"{oras} are {locuitori} locuitori");
    else
        Console.WriteLine($"Format greșit: {text}");
}

static bool IncearcaCitire(string text, out string oras, out int locuitori)
{
    string[] parti = text.Split(':');
    oras = parti[0];
    locuitori = 0;
    return parti.Length == 2 && int.TryParse(parti[1], out locuitori);
}

Rezultat

Iași are 700000 locuitori
Format greșit: Cluj
Format greșit: Arad:fără

Greșeli frecvente

  • Uitarea cuvântului ref sau out la apel: AdaugaRef(sold, 50) nu se compilează.
  • O metodă cu out care iese fără să atribuie parametrul dă eroarea CS0177.
  • Metodele async și iteratorii (yield) nu pot avea parametri ref, out sau in. Returnează un tuple în loc.

Comenzi înrudite

  • Metode (static, void, return): O metodă grupează cod reutilizabil sub un nume: primește parametri, poate returna o valoare cu return sau nimic (void).
  • int.Parse și int.TryParse: Transformă text în număr: Parse aruncă excepție la text invalid, TryParse returnează false și nu aruncă.
  • Tuple-uri și deconstrucție: Grupează câteva valori fără să declari o clasă: (string Nume, int Varsta), cu deconstrucție în variabile separate.
  • struct: Tip valoare: se copiază la atribuire și la transmitere, nu are null și e potrivit pentru date mici și imutabile.