Metodi, parametri e overloading in C#
Man mano che un programma cresce, ripetere lo stesso blocco di istruzioni in più punti diventa insostenibile. I metodi risolvono questo problema permettendo di raggruppare un insieme di istruzioni sotto un nome, per poi richiamarle ogni volta che servono. Un metodo ben progettato rende il codice riutilizzabile, leggibile e più facile da mantenere. In questo articolo vediamo come definire i metodi, come passare loro dei dati e come sfruttare le funzionalità che C# offre per renderli flessibili.
Definire e richiamare un metodo
Un metodo è caratterizzato da una firma composta dal tipo di ritorno, dal nome e dall'elenco dei parametri. Il tipo di ritorno indica quale valore il metodo restituisce; se non ne restituisce alcuno, si usa la parola chiave void.
// Un metodo che non restituisce nulla
static void Greet(string name)
{
Console.WriteLine($"Ciao, {name}!");
}
// Un metodo che restituisce un intero
static int Add(int a, int b)
{
return a + b;
}
// Richiamo dei metodi
Greet("Gabriele");
int total = Add(3, 4); // total vale 7
La parola chiave return termina l'esecuzione del metodo e, quando previsto, restituisce un valore al chiamante. In un metodo void si può usare return da solo per uscire in anticipo.
Parametri e argomenti
I parametri sono le variabili dichiarate nella firma del metodo, mentre gli argomenti sono i valori effettivi passati durante la chiamata. Per impostazione predefinita, i parametri di tipo valore vengono passati per copia: eventuali modifiche all'interno del metodo non si riflettono sulla variabile originale del chiamante.
static void TryToChange(int value)
{
value = 100; // modifica solo la copia locale
}
int original = 5;
TryToChange(original);
Console.WriteLine(original); // Stampa ancora 5
Parametri opzionali e nominali
C# consente di assegnare un valore predefinito a un parametro, rendendolo opzionale: se il chiamante non fornisce un argomento, viene usato il valore predefinito. Gli argomenti nominali, invece, permettono di specificare a quale parametro corrisponde ciascun valore indicandone il nome, migliorando la leggibilità e liberando dall'ordine posizionale.
// power ha un valore predefinito
static double Raise(double baseValue, int power = 2)
{
return Math.Pow(baseValue, power);
}
double square = Raise(5); // usa power = 2, risultato 25
double cube = Raise(5, 3); // power = 3, risultato 125
double custom = Raise(power: 4, baseValue: 2); // argomenti nominali
Passaggio per riferimento: ref, out e in
In alcuni casi è utile che un metodo modifichi direttamente la variabile del chiamante o restituisca più valori. C# offre tre modificatori a questo scopo. Il modificatore ref passa la variabile per riferimento, permettendo al metodo di leggerla e modificarla. Il modificatore out è pensato per restituire valori: il metodo deve assegnare il parametro prima di terminare. Il modificatore in passa per riferimento in sola lettura, utile per evitare copie di strutture di grandi dimensioni.
// ref: legge e modifica la variabile originale
static void Double(ref int number)
{
number *= 2;
}
// out: restituisce piu valori
static void Divide(int dividend, int divisor, out int quotient, out int remainder)
{
quotient = dividend / divisor;
remainder = dividend % divisor;
}
int value = 10;
Double(ref value);
Console.WriteLine(value); // Stampa 20
Divide(17, 5, out int q, out int r);
Console.WriteLine($"Quoziente {q}, resto {r}"); // Quoziente 3, resto 2
Un numero variabile di argomenti: params
Quando un metodo deve accettare un numero non predeterminato di argomenti dello stesso tipo, la parola chiave params consente di raccoglierli in un array. Il chiamante può passare tanti argomenti quanti ne desidera, separati da virgola.
// Accetta un numero qualsiasi di interi
static int Sum(params int[] numbers)
{
int total = 0;
foreach (int n in numbers)
{
total += n;
}
return total;
}
int result = Sum(1, 2, 3, 4, 5); // result vale 15
Overloading dei metodi
L'overloading permette di definire più metodi con lo stesso nome ma con parametri diversi per numero o tipo. Il compilatore sceglie automaticamente la versione corretta in base agli argomenti forniti durante la chiamata. Questo consente di offrire varianti coerenti di una stessa operazione.
// Tre versioni dello stesso metodo con firme diverse
static int Multiply(int a, int b)
{
return a * b;
}
static double Multiply(double a, double b)
{
return a * b;
}
static int Multiply(int a, int b, int c)
{
return a * b * c;
}
Console.WriteLine(Multiply(2, 3)); // usa la prima versione
Console.WriteLine(Multiply(2.5, 4.0)); // usa la seconda
Console.WriteLine(Multiply(2, 3, 4)); // usa la terza
È importante notare che l'overloading si basa sui parametri: due metodi non possono differire solo per il tipo di ritorno, perché il compilatore non avrebbe modo di distinguerli.
Metodi a corpo di espressione
Quando un metodo si limita a restituire il risultato di una singola espressione, C# offre una sintassi compatta chiamata expression-bodied, che sostituisce le parentesi graffe e la parola chiave return con il simbolo =>.
// Forma compatta di un metodo a espressione singola
static int Square(int x) => x * x;
static string Describe(bool flag) => flag ? "attivo" : "inattivo";
Funzioni locali
Una funzione locale è un metodo definito all'interno di un altro metodo. È utile quando si vuole incapsulare una porzione di logica ausiliaria che ha senso solo nel contesto del metodo che la contiene, mantenendola vicina al punto in cui viene usata.
static int Factorial(int n)
{
// Funzione locale ricorsiva, visibile solo qui
int Compute(int value)
{
if (value <= 1)
{
return 1;
}
return value * Compute(value - 1);
}
return Compute(n);
}
Conclusione
Abbiamo imparato a organizzare il codice in metodi, definendone la firma con tipo di ritorno, nome e parametri, e a richiamarli passando argomenti. Abbiamo esplorato i parametri opzionali e nominali, il passaggio per riferimento con ref, out e in, gli argomenti a lunghezza variabile con params e l'overloading che consente più varianti di uno stesso metodo. Infine abbiamo visto le forme sintetiche a corpo di espressione e le funzioni locali. Nel prossimo articolo affronteremo array, stringhe e collezioni, gli strumenti per lavorare con insiemi di dati.