CodeGym /Corsi /C# SELF /Modificatori di parametri ( r...

Modificatori di parametri ( ref, out, in)

C# SELF
Livello 12 , Lezione 4
Disponibile

1. Introduzione

In C# di default tutti i parametri dei metodi vengono passati per valore, cioè al metodo arriva una copia, non l'originale.

Ma che succede se vuoi cambiare una variabile esterna dall'interno del metodo? O restituire più valori dal metodo? O magari devi passare dentro al metodo una struttura enorme, ma non vuoi copiare tutto questo "valigione senza manico", e ti basta passare solo il riferimento per risparmiare memoria?

Tutto questo riguarda i modificatori di parametri. Ti danno il controllo sul "percorso di trasmissione" dei dati tra il codice chiamante e il metodo.

Tipi di modificatori

Modificatore Trasmissione dati Serve inizializzare prima della chiamata? Si può leggere dentro? Si può scrivere dentro? Scenari tipici d'uso
ref In entrambe le direzioni Passare una variabile per lettura e/o modifica
out Solo verso l'esterno No No (prima dell'assegnazione) Sì (obbligatorio!) Restituire più valori da un metodo
in Solo verso l'interno No Passare strutture grandi per "sola lettura" risparmiando memoria

Tranquillo, ora li vediamo uno per uno. È molto più semplice di quanto sembri.

2. Modificatore ref: Passiamo per riferimento: sia lettura che scrittura

Immagina: porti una tazza di caffè a un amico e dici — "Ecco, puoi berlo, riscaldarlo, o anche aggiungerci qualcosa". L'amico può fare quello che vuole con quel caffè, e quello che rimane (o non rimane) — torna a te. Così funziona ref.


void DoSomething(ref int x)
{
    x = x + 10; // Modifichiamo il parametro in ingresso
}
        
Metodo con parametro ref

Per passare un parametro come ref, va dichiarato con lo stesso modificatore sia nella dichiarazione che nella chiamata del metodo:

int mioNumero = 5;
DoSomething(ref mioNumero);
Console.WriteLine(mioNumero); // stampa 15

Deve essere per forza una variabile, non puoi passare un'espressione per riferimento. Questo codice non funziona:

DoSomething(ref 10);		// qui ci vuole una variabile!
  • Prima della chiamata: la variabile deve essere inizializzata (deve avere un valore).
  • Dentro il metodo: puoi leggere e modificare il valore.
  • Dopo la chiamata: le modifiche restano.

Esempio: Scambiare i valori di due variabili

A volte serve scambiare i valori di due variabili. Ma c'è una particolarità: in C# i tipi valore (tipo int) di default vengono passati per valore. Questo significa che quando li passi a un metodo, vengono copiate le copie dei valori, non i riferimenti alle variabili. Per permettere al metodo di modificare le variabili chiamanti, serve la parola chiave ref.

Esempio senza ref — non funziona:

// Tentativo di scambiare i valori — non funziona
void Swap(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;
}

int x = 10;
int y = 20;
Swap(x, y); // Passa le copie dei valori
Console.WriteLine($"{x}, {y}"); // → 10, 20 — non è cambiato nulla!

Dentro la funzione Swap le variabili a e b sono copie di x e y. Cambiamo solo queste copie, gli originali restano intatti.

Esempio con ref — le variabili si scambiano


// Scambiamo i valori delle variabili usando ref
void Swap(ref int a, ref int b)
{
    int temp = a;
    a = b;
    b = temp;
}

int x = 10;
int y = 20;
Swap(ref x, ref y); // Passa i riferimenti alle variabili
Console.WriteLine($"{x}, {y}"); // → 20, 10 — tutto ok
        

ref permette di passare i riferimenti alle variabili stesse, non i loro valori.
Quindi la funzione Swap lavora direttamente con x e y, cambiando il loro contenuto.

Quando usare ref?

  • Quando vuoi modificare la variabile passata (tipo aumentarla, sostituirla).
  • Non serve restituire un nuovo valore (come con return), perché vuoi cambiare direttamente l'argomento.
  • Non usarlo per "scaricare" un sacco di valori diversi — per quello c'è un altro modificatore.

3. Modificatore out: Passiamo verso l'esterno

Ora immagina: vai da un amico, ma hai la tazza vuota, e chiedi: "Riempi la tazza — con quello che vuoi, caffè o tè!" Dopo di che il tuo amico deve per forza riempirla, altrimenti il compilatore si arrabbia.

out serve per far sì che un metodo possa restituire più valori diversi, senza usare il valore di ritorno. Questi parametri devono essere per forza inizializzati dentro il metodo.


void GetCoordinates(out int x, out int y)
{
    x = 5;
    y = 10; // Senza questo - errore!
}
        
Metodo con parametri out
  • Prima della chiamata: la variabile può non essere inizializzata, puoi anche scrivere semplicemente out int x nella chiamata.
  • Dentro il metodo: devi per forza assegnare un valore prima di uscire dal metodo, altrimenti il compilatore fa sciopero.
  • Dopo la chiamata: la variabile contiene il nuovo valore.

Esempio: Restituire due valori dal metodo

void ParseNameAndAge(string input, out string nome, out int eta)
{
    string[] parti = input.Split(',');	// trasformiamo la stringa in array di stringhe - dividiamo per ','
    nome = parti[0];
    eta = int.Parse(parti[1]);
}

string inputUtente = "Ivan,25";
ParseNameAndAge(inputUtente, out string nomeUtente, out int etaUtente);
Console.WriteLine($"{nomeUtente} — {etaUtente} anni");

Quando usare out?

  • Quando vuoi restituire più valori dal metodo (tipo dividere una stringa in parti, come sopra).
  • Quando il tipo di ritorno non è noto in anticipo (tipo tentare di convertire una stringa in numero).

Esempi da .NET: Il metodo int.TryParse(string, out int) — un classico!

string input = "123";
if (int.TryParse(input, out int numeroConvertito))
{
    Console.WriteLine($"Convertito: {numeroConvertito}");
}
else
{
    Console.WriteLine("Errore di conversione!");
}

Il metodo int.TryParse restituisce true se riesce a convertire la stringa in numero e false se non ci riesce. Il valore numerico lo restituisce tramite out.

Errori tipici e situazioni buffe con ref e out

  • Hai dimenticato di inizializzare la variabile per ref — il compilatore si arrabbia.
  • Non hai assegnato un valore per out — il compilatore è ancora più arrabbiato.
  • Hai dimenticato di specificare il modificatore nella chiamata: hai scritto DoSomething(x) invece di DoSomething(ref x).
  • Usare ref o out con costanti o letterali non si può! Solo variabili. Solo loro hanno un indirizzo in memoria e puoi creare un riferimento a quello.

4. Modificatore in: Sola lettura, solo per riferimento

A volte capita che devi passare a un metodo un oggetto grande e complesso. Di solito un oggetto così viene passato per riferimento, ma così può essere modificato accidentalmente dalla funzione che ha accesso completo — perché gli hai dato il riferimento.

In teoria potresti fare una copia completa dell'oggetto e passarla. Così nessuno cambia l'originale. Ma se l'oggetto è molto grande, copieresti un sacco di dati inutilmente. Molto meglio passare l'oggetto alla funzione e chiedere al compilatore di controllare che nessuno lo modifichi.

in — è un modificatore nuovo che permette di passare strutture per riferimento, ma solo per lettura. È come "una spada preziosa sotto vetro": puoi guardare, ma non puoi toccare.


void PrintPoint(in Point pt)
{
    pt.X = 5; // Errore! Sola lettura.
    Console.WriteLine($"Punto: {pt.X}, {pt.Y}");  //Questo si può
}
        
Metodo con parametro in (sola lettura)
  • Prima della chiamata: la variabile deve essere inizializzata.
  • Dentro il metodo: puoi solo leggere, non puoi modificare.
  • Risparmio memoria: la struttura non viene copiata, ma passata per riferimento.

Si usa, ad esempio, per array di strutture grandi, per evitare copie inutili. Ma con le classi (tipi reference) in non fa molta differenza.

Esempio: Passare una struttura grande per riferimento, senza copiarla

struct BigData
{
    public int A, B, C, D, E;
}

void PrintBigData(in BigData dati)
{
    dati.A = 10; // non si può - sola lettura!
    Console.WriteLine(dati.A + dati.B + dati.C + dati.D + dati.E);
}

BigData mieiDati = new BigData { A = 10, B = 20, C = 30, D = 40, E = 50 };
PrintBigData(in mieiDati);

5. Domande che spesso preoccupano:

Posso usare ref/out/in con array e tipi reference?
Sì, puoi! Ma ricorda: gli array sono già tipi reference. Passando un array con ref, puoi cambiare il riferimento stesso. Di solito serve per le strutture.

Qual è la differenza tra ref e out, se posso ottenere il valore in entrambi i casi?
Con ref la variabile deve essere inizializzata prima di usarla. Con out non serve, ma dentro il metodo DEVE essere assegnata almeno una volta.

Cosa succede se provo a usare un letterale (tipo il numero 5) con ref/out/in?
Il compilatore dà subito errore. Solo variabili!

Quanti parametri posso fare ref/out/in?
Quanti vuoi, nessun limite! Ma non esagerare per la leggibilità: se ne hai più di due o tre, forse è meglio usare una tupla, una struttura o una classe.

1
Sondaggio/quiz
Classi e oggetti, livello 12, lezione 4
Non disponibile
Classi e oggetti
Introduzione all'ereditarietà
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION