Menu

Metodi in C#: parametri, valori di ritorno, params, overloading e Main

Come dichiarare e chiamare metodi in C#: tipi di ritorno e void, parametri, metodi con corpo di espressione, restituire più valori con una tupla, overloading, array params, metodi statici e di istanza, il metodo Main, ricorsione e funzioni locali.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Un metodo è un blocco di codice con un nome che riceve dei dati in ingresso (parametri), svolge il suo lavoro e può restituire un risultato (il valore di ritorno). In C# ogni metodo vive dentro un tipo; non esistono funzioni indipendenti.

Output:

Headphones   48.00
Cable        11.40

Anatomia di un metodo

public static decimal PriceWithTax(decimal net, decimal taxRate)
{
    return net * (1 + taxRate);
}
  • Modificatore di accesso (public, private, ...): chi può chiamarlo. Se manca, il metodo è private al suo tipo.
  • static: il metodo appartiene al tipo, non a un oggetto. Ne parliamo più avanti.
  • Tipo di ritorno: il tipo del valore restituito, oppure void se non restituisce nulla.
  • Nome: in PascalCase per convenzione, di solito un verbo (Calculate, SendEmail, TryParse).
  • Lista dei parametri: zero o più coppie type name. Ogni parametro è una variabile locale inizializzata con l'argomento passato dal chiamante.
  • Corpo: le istruzioni tra le parentesi graffe.

Il nome più i tipi dei parametri (e gli eventuali modificatori ref, out o in) formano la firma del metodo. Il tipo di ritorno non ne fa parte, e questo conta per l'overloading.

Valori di ritorno e void

return termina subito il metodo e restituisce un valore. Un metodo con un tipo di ritorno deve restituire un valore di quel tipo su ogni percorso:

static string Grade(int score)
{
    if (score >= 50)
        return "pass";
}   // error CS0161: 'Program.Grade(int)': not all code paths return a value

Il compilatore controlla ogni ramo. Aggiungi il return "fail"; mancante, oppure un throw se arrivare a quel punto è un bug.

Un metodo void non restituisce nulla. Può comunque usare un semplice return; per uscire prima:

Output:

ana@mail.com: newsletter sent
ben@mail.com: not subscribed, skipped
False
True

Metodi con corpo di espressione

Quando il corpo di un metodo è una singola espressione, => sostituisce le graffe e il return:

static decimal Discount(decimal price) => price * 0.1m;
static bool IsAdult(int age) => age >= 18;
static void Log(string message) => Console.WriteLine($"[log] {message}");

È lo stesso metodo, scritto in modo più breve. Funziona anche per i metodi void, purché il corpo sia una sola espressione (una chiamata, un'assegnazione).

I parametri sono copie

Gli argomenti vengono passati per valore di default: il parametro riceve una copia dell'argomento. Assegnare un valore al parametro dentro il metodo non modifica la variabile del chiamante:

Output:

inside: 42
after:  21
scores: 70, 85, 100

La List è cambiata perché per un tipo riferimento il valore copiato è il riferimento: il metodo e il chiamante puntano allo stesso oggetto lista. Riassegnare scores = new List<int>() dentro il metodo non toccherebbe il chiamante. Per permettere a un metodo di assegnare la variabile del chiamante usa ref o out; per dare ai parametri valori predefiniti e passarli per nome usa i parametri opzionali e gli argomenti con nome.

Restituire più valori con una tupla

Da C# 7.0 un metodo può restituire una tupla con elementi dotati di nome, e il chiamante può decostruirla in variabili separate:

Output:

min 15, max 27, avg 21.0
range 12

_ scarta l'elemento che non ti serve. Per risultati usati in molti punti, una piccola classe o un record con proprietà dotate di nome descrive la struttura meglio di una tupla.

Overloading

Più metodi possono condividere un nome se le loro liste di parametri differiscono per numero, tipi o ordine dei tipi. Il compilatore sceglie la corrispondenza migliore per ogni chiamata:

Output:

12.57
13.50
25

Area(5) sceglie la versione int perché è una corrispondenza esatta; Area(2.0) sceglie quella double. I metodi non possono differire solo per il tipo di ritorno: due metodi Area(int) che restituiscono int e double danno l'errore CS0111, Type 'Program' already defines a member called 'Area' with the same parameter types, perché una chiamata come Area(5) non saprebbe distinguerli.

Anche una chiamata che nessun overload accetta fallisce in compilazione: Area(1, 2, 3) è CS1501, No overload for method 'Area' takes 3 arguments, e Area("big") è CS1503, Argument 1: cannot convert from 'string' to 'double'.

params: un numero variabile di argomenti

Un parametro array params accetta un numero qualsiasi di argomenti, anche zero. Il compilatore costruisce l'array per te:

Output:

empty cart: 0 item(s), 0
one item: 1 item(s), 9.99
three items: 3 item(s), 19.75
from array: 2 item(s), 50

Regole: params deve essere l'ultimo parametro (altrimenti CS0231), un metodo ne ha al massimo uno, e passare un array esistente consegna quell'array così com'è. Console.WriteLine("{0} and {1}", a, b) e string.Format sono metodi params object[]. C# 13 ha esteso params ad altri tipi di collezione come List<T> e ReadOnlySpan<T>; prima erano ammessi solo gli array.

Metodi statici e di istanza

Un metodo static appartiene al tipo e si chiama sul nome del tipo: Math.Max(3, 7), Program.Total(...). Un metodo di istanza appartiene a un oggetto e può usare i campi di quell'oggetto:

Output:

12

Chiamare un metodo di istanza da uno statico senza un oggetto è l'errore da principiante più comune in C#:

class Program
{
    void Greet() => Console.WriteLine("hi");

    static void Main()
    {
        Greet();
        // error CS0120: An object reference is required for the non-static field, method, or property 'Program.Greet()'
    }
}

Marca Greet come static, oppure crea un oggetto e chiamalo su quello: new Program().Greet();. La pagina su static tratta più a fondo i membri statici e le classi statiche.

Il metodo Main

Main è il punto in cui parte un programma. Deve essere static, e queste firme sono valide:

static void Main() { }
static void Main(string[] args) { }            // command-line arguments
static int Main(string[] args) { return 0; }   // exit code for the shell
static async Task Main() { }                    // C# 7.1 and later

La forma async Task Main permette al punto di ingresso di usare direttamente await (vedi async e await). Da C# 9 un file può invece contenere istruzioni di primo livello: codice senza alcuna classe né Main, come in un nuovo progetto dotnet new console. Il compilatore genera la classe e il metodo Main attorno a esso, quindi è la stessa cosa scritta in modo più breve; args resta disponibile.

Ricorsione

Un metodo può chiamare se stesso. Ogni chiamata ha i propri parametri e le proprie variabili locali, e un caso base deve fermare la catena:

Output:

120
2432902008176640000
35

Senza un caso base, o con un input troppo profondo, ogni chiamata aggiunge un frame allo stack finché il programma muore con StackOverflowException. Quell'eccezione non si può catturare, quindi per input di profondità sconosciuta riscrivi la ricorsione come un ciclo.

Funzioni locali

C# 7.0 ha introdotto le funzioni locali: metodi dichiarati dentro un altro metodo e visibili solo lì. Possono usare le variabili del metodo che le contiene:

// C# 7.0 and later
static int CountValid(string[] codes)
{
    int count = 0;
    foreach (string code in codes)
    {
        if (IsValid(code)) count++;
    }
    return count;

    bool IsValid(string c) => c.Length == 6 && char.IsLetter(c[0]);
}

Tengono una funzione di supporto accanto al suo unico chiamante senza esporla al resto della classe. Da C# 8 si possono marcare come static per vietare la cattura di variabili esterne. Dove una funzione locale non è disponibile, un metodo privato statico nella stessa classe fa lo stesso lavoro, come negli altri esempi di questa pagina.

Domande frequenti

Che differenza c'è tra metodo e funzione in C#?

In C# ogni funzione è dichiarata all'interno di una classe, struct o record, e si chiama metodo. Non esistono funzioni indipendenti. Anche le istruzioni di primo livello (C# 9) e le funzioni locali (C# 7) vengono compilate in metodi di un tipo generato o contenitore. "Funzione" si usa in modo informale, e per lambda e delegate.

Cosa fa params in C#?

params permette a un metodo di ricevere un numero variabile di argomenti di un tipo: static int Sum(params int[] numbers) si può chiamare come Sum(), Sum(4), Sum(4, 5, 6) o Sum(existingArray). Il compilatore raccoglie gli argomenti in un array. Il parametro params deve essere l'ultimo, e un metodo può averne solo uno.

Cos'è il metodo Main in C#?

Main è il punto di ingresso del programma: il metodo che il runtime chiama per primo. Deve essere static, può restituire void o int (il codice di uscita) e può ricevere string[] args per gli argomenti da riga di comando. Da C# 7.1 può anche essere async Task Main, e da C# 9 le istruzioni di primo livello lasciano che sia il compilatore a generarlo per te.

Come restituisco più valori da un metodo C#?

Restituisci una tupla: static (int Min, int Max) Range(int[] values) e return (min, max);. Chi chiama può leggere result.Min o decostruire con var (lo, hi) = Range(values);. Per valori che viaggiano sempre insieme nel tuo codice, una piccola classe o un record è più chiaro; i parametri out sono l'alternativa più vecchia.

Perché ottengo "An object reference is required for the non-static field, method, or property"?

Un metodo static come Main ha chiamato direttamente un metodo di istanza. I metodi statici appartengono al tipo e non hanno un oggetto su cui chiamarlo. Marca come static il metodo chiamato, oppure crea prima un oggetto: var app = new Program(); app.Greet();.

Cosa significa "not all code paths return a value"?

Errore CS0161: un metodo con un tipo di ritorno ha almeno un percorso che arriva alla parentesi graffa di chiusura senza un return. Di solito un if restituisce un valore ma dopo non c'è un else né un return finale. Aggiungi un return (o un throw) alla fine del metodo.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA