Development

Strumenti, idee e visione per diventare uno sviluppatore consapevole, ambizioso e pronto a costruire il proprio futuro con coraggio e competenza.

Metodi in C# spiegati con esempi pratici e memoria interna

Metodi in C#: guida completa con esempi, memoria e funzionamento interno

Scritto da Marco Morello il 17 maggio 2025

Ricordo ancora quando ho incontrato per la prima volta i "metodi" in C#. Ero agli inizi, curioso ma anche confuso.

Ogni volta che vedevo qualcosa come Console.WriteLine(), mi chiedevo:

"Ma cosa significa quel nome, quel punto, quelle parentesi?"

Era come vedere un bottone su un macchinario complesso, senza sapere cosa avrebbe fatto una volta premuto.

E forse è anche la tua sensazione adesso. Hai già visto tanti metodi in giro, li hai usati in qualche esercizio, magari hai persino copiato e incollato del codice che li conteneva... ma non ti è chiaro cosa sono davvero.

👉 Questo articolo è qui per cambiare le cose.

Non troverai definizioni accademiche o discorsi da manuale scolastico. Troverai invece:

Ogni concetto che leggerai qui è parte di un percorso più ampio, per comprendere a fondo questo costrutto fondamentale del linguaggio orientato agli oggetti (OOP).

Ma entriamo subito nel vivo senza tanti giri di parole!

Cos'è un metodo in C#

Prima ancora di guardare il codice, è importante capire l’idea di fondo: un metodo è una funzione che appartiene a una classe.

È un pezzo di logica che abbiamo isolato, definito, a cui abbiamo dato un nome. Serve per fare qualcosa. Punto.

Esempi semplici possono essere:

I metodi sono gli strumenti principali che usi per dire al computer cosa fare. Sono le tue "azioni" nel codice.

E ogni metodo ha tre componenti fondamentali:

Per esempio:

int Somma(int a, int b)
    {
        return a + b;
    }

Questo metodo si chiama Somma, prende due numeri a e b e restituisce un int, cioè un numero intero.

✔️ Semplice? Forse. Ma qui c’è molto più di quanto sembri.

Dietro questa struttura si nasconde tutto un mondo fatto di regole, ottimizzazioni, differenze sottili e scelte progettuali che faranno la differenza tra un codice amatoriale e uno degno di una software house.

Ecco perché, nelle prossime sezioni, andremo molto più in profondità.

Parleremo non solo di cosa fanno i metodi, ma di cosa succede quando li esegui, dove vivono in memoria, cosa succede ai parametri, come cambia tutto quando usi ref o out (e cosa sono), e così via.

Con pazienza, ma senza giri di parole. A me piacciono molto le cose pratiche e pragmatiche più che la teoria, per quanto sia comunque importante.

Continua a leggere e capirai cosa intendo.

Come funzionano i metodi in C#: dietro le quinte

Ora che abbiamo capito cos'è un metodo, è il momento di vedere cosa accade realmente quando lo chiamiamo. Perché un metodo non è solo un blocco di codice che "si esegue". È una vera e propria operazione sulla memoria, sul flusso del programma e sulla gestione dello stato interno.

Vediamo i passaggi fondamentali:

  1. Il programma incontra la chiamata al metodo e mette in pausa l'esecuzione lì dov'è.
  2. I parametri vengono preparati e allocati.
  3. Viene creato un nuovo "contenitore" nello stack di memoria, chiamato stack frame.
  4. Dentro questo contenitore vivranno le variabili locali del metodo e i valori dei parametri.
  5. Il metodo esegue tutto il suo codice, riga per riga.
  6. Quando trova un return (oppure arriva alla fine), restituisce il controllo al punto in cui era stato invocato.
  7. Il contenitore temporaneo nello stack viene rimosso.

🎯 Questo ciclo si ripete ogni volta che chiami un metodo. Anche se chiami due volte lo stesso metodo, vengono create due istanze distinte nello stack.

Stack e Heap: differenze chiave

Lo stack è veloce, ordinato, viene gestito come una pila. Ogni metodo ha il suo spazio e, quando finisce, quello spazio viene eliminato.

L' heap invece è una zona di memoria più ampia e disordinata, dove finiscono gli oggetti creati con new. Gli oggetti qui rimangono in vita anche dopo la fine del metodo, finché il garbage collector non li rimuove.

📘 Se vuoi approfondire la differenza tra stack e heap, ne parleremo nel dettaglio in un articolo dedicato. Intanto, puoi iniziare da qui:

🔗 Microsoft Docs – Garbage Collection Fundamentals

Esempio concreto

void Test()
    {
        int a = 10; // vive nello stack
        string messaggio = "Ciao"; // il riferimento è nello stack, ma la stringa è nell'heap
    }

📌 Anche se non li vedi, questi movimenti in memoria succedono ogni volta.

Capire come funziona un metodo nella memoria ti aiuta a scrivere codice più efficiente, ma soprattutto più prevedibile.

Nella prossima sezione vedremo i diversi tipi di metodi che puoi scrivere in C# e come sceglierli in base al tuo scopo.

Metodi void e metodi con return

Uno dei primi bivi concettuali che affronti quando inizi a scrivere metodi è questo:

👉 Deve restituire qualcosa o no?

Questa semplice domanda nasconde una decisione cruciale, perché divide i metodi in due grandi categorie e influenza come progetti il tuo codice, come lo testi e come lo riutilizzi.

Un metodo void è come un comando che impartisci al tuo programma: dice al computer "fai qualcosa", ma non restituisce alcun valore.

È come premere il pulsante di una lampada: la accendi, ma non ti aspetti che ti dica qualcosa in risposta.

Sono perfetti per tutte quelle situazioni in cui vuoi:

🔹 Esempio pratico:

void Saluta()
    {
        Console.WriteLine("Ciao utente!");
    }

📌 Questo metodo esegue un’azione ben precisa: stampa a schermo. Ma, finita la sua esecuzione, non lascia traccia, né dati da raccogliere.

È utile quando il focus è sul “fare” piuttosto che sul “calcolare o restituire”.

Dall’altra parte ci sono i metodi che invece producono un risultato. Questi usano il comando return per restituire un valore al codice che li ha invocati.

📍 In questi casi, al posto di void, specifichi il tipo di dato restituito: può essere un numero (int), una stringa (string), un oggetto o qualsiasi altro tipo.

💡 Esempio chiaro:

string GeneraMessaggio(string nome)
    {
        return $"Benvenuto, {nome}!";
    }

Ora puoi usare il valore restituito in altri contesti del programma:

string messaggio = GeneraMessaggio("Luca");
    Console.WriteLine(messaggio);

✅ Il metodo ha generato un messaggio, e il codice chiamante lo ha potuto usare subito per stamparlo a video.

Questo tipo di metodo è fondamentale quando vuoi separare la logica dal comportamento: la funzione “calcola” qualcosa, ma non decide cosa farne. Questo è compito di chi la chiama.

Per aiutarti nella scelta, poniti sempre questa domanda:

❓ Mi serve ottenere un valore da questo metodo per continuare l’elaborazione?

⚠️ Ma attenzione: non si tratta solo di sintassi.

È una scelta architetturale, che cambia il modo in cui il tuo codice:

✅ I metodi con return

❌ I metodi void vanno bene quando:

🧠 Impara a usare entrambi in modo consapevole. Il miglior codice nasce da scelte intenzionali, non da abitudini o automatismi.

Nella prossima sezione vedremo un'altra distinzione fondamentale: come vengono passati i parametri ai metodi, e perché la differenza tra "per valore" e "per riferimento" può avere un impatto enorme sul comportamento del tuo codice.

Parametri nei metodi C#: per valore, per riferimento e di default

Ogni volta che chiami un metodo, spesso gli passi delle informazioni. Quelle informazioni si chiamano parametri e il modo in cui li passi può cambiare profondamente il comportamento del tuo codice.

Capire come funziona il passaggio dei parametri in C# ti aiuta ad evitare:

Ma soprattutto ti permette di scrivere codice più chiaro e corretto

Vediamo insieme le tre modalità principali:

🔹 Parametri passati per valore (by value)

Quando passi un parametro "normale" a un metodo, C# crea una copia del valore e lavora su quella.

📌 Questo significa che:

💡 Esempio:

void Raddoppia(int numero)
    {
        numero *= 2;
    }

    int x = 10;
    Raddoppia(x);
    Console.WriteLine(x); // Output: 10

Anche se dentro il metodo numero viene raddoppiato, fuori il valore originale di x resta invariato.

✅ Questo approccio è sicuro e semplice, ma non sempre adatto se vuoi modificare il valore originale.

🔁 Parametri passati per riferimento: ref e out

Se vuoi che un metodo modifichi direttamente una variabile esterna, puoi passare il parametro per riferimento usando le parole chiave ref o out.

ref: passo un valore già inizializzato
void Raddoppia(ref int numero)
    {
        numero *= 2;
    }

    int x = 10;
    Raddoppia(ref x);
    Console.WriteLine(x); // Output: 20

Con ref, il metodo lavora direttamente sulla variabile originale. Qualsiasi modifica è visibile anche fuori.

out: passo un contenitore da riempire

out funziona come ref, ma serve per ritornare un valore attraverso un parametro. La differenza è che non devi inizializzarlo prima della chiamata.

void CalcolaArea(int lato, out int area)
    {
        area = lato * lato;
    }

    int risultato;
    CalcolaArea(5, out risultato);
    Console.WriteLine(risultato); // Output: 25

📍 Usalo quando un metodo deve restituire più di un valore o quando vuoi evitare return multipli.

🔗 Documentazione ufficiale Microsoft: Passing Parameters

⚙️ Parametri con valore di default

A volte vuoi che un parametro sia opzionale. C# ti permette di assegnare un valore di default, che verrà usato se il chiamante non ne fornisce uno.

void Saluta(string nome = "amico")
    {
        Console.WriteLine($"Ciao, {nome}!");
    }

    Saluta();           // Ciao, amico!
    Saluta("Federica"); // Ciao, Federica!

✅ Questo rende i tuoi metodi più flessibili e puliti, evitando overload inutili.

🎯 I parametri con valore predefinito vanno sempre dopo quelli obbligatori.

Nella prossima sezione affronteremo uno strumento fondamentale per scrivere codice leggibile e riutilizzabile: l’overloading dei metodi.

Prepara la mente, perché ci avviciniamo a una delle caratteristiche più potenti e sottovalutate di C#.

Overloading dei metodi: uno stesso nome, più comportamenti

Immagina di voler creare un metodo chiamato Saluta. A volte vuoi usarlo senza parametri, altre volte vuoi passargli un nome, o magari anche un orario del giorno.

Devi inventarti ogni volta un nome diverso come Saluta1, SalutaConNome, SalutaConNomeEOra? 🤨

Assolutamente no.

Con l’overloading dei metodi puoi usare lo stesso nome, cambiando solo numero e tipo di parametri.

È come avere più chiavi con la stessa etichetta, ma che aprono porte diverse a seconda della forma.

🔄 Come funziona

Overloading (in italiano sovraccarico) significa definire più metodi con lo stesso nome, ma con firme diverse. (Vedi documentazione ufficiale Microsoft)

La “firma” di un metodo è composta da:

📌 Il tipo di ritorno non fa parte della firma, quindi non puoi avere due metodi con lo stesso nome e parametri ma solo tipo di ritorno diverso.

💡 Esempio pratico

void Saluta()
    {
        Console.WriteLine("Ciao!");
    }

    void Saluta(string nome)
    {
        Console.WriteLine($"Ciao, {nome}!");
    }

    void Saluta(string nome, string orario)
    {
        Console.WriteLine($"Buon {orario}, {nome}!");
    }

Questi tre metodi convivono tranquillamente nello stesso file. Il compilatore capisce quale usare in base ai parametri che passi.

Saluta(); // Ciao!
Saluta("Giulia"); // Ciao, Giulia!
Saluta("Marco", "pomeriggio"); // Buon pomeriggio, Marco!

✅ Questo ti permette di chiamare sempre lo stesso metodo Saluta() anche quando vuoi passargli parametri diversi. Così non devi ricordarti nomi diversi per ogni caso, e il codice risulta più chiaro e naturale da leggere.

❌ Esempio sbagliato: overloading solo sul tipo di ritorno

int Calcola() { return 1; }
string Calcola() { return "uno"; } // Errore!

👉 Il compilatore non saprà quale scegliere, perché la firma è identica (nessun parametro) e cambia solo il tipo di ritorno.

L’overloading è utile quando:

⚠️ Quando evitarlo

Se hai troppi overload con combinazioni simili, può generare confusione.

In questi casi, meglio valutare:

📌 L’overloading è uno di quegli strumenti che rende il tuo codice elegante... se usato bene.

Ti permette di parlare con il tuo programma con naturalezza, come se stessi spiegando qualcosa a voce.

📚 Approfondimento utile: l’overloading è polimorfismo statico

Quando parliamo di overloading, stiamo in realtà applicando una forma particolare di polimorfismo, chiamata polimorfismo statico (o di compilazione).

Polimorfismo significa “molte forme”, e si riferisce alla capacità del linguaggio di trattare metodi o oggetti in modi diversi a seconda del contesto.

Nel caso dell’overloading, hai più metodi con lo stesso nome, ma con firme diverse (cioè numero o tipo di parametri diversi), e il compilatore è in grado di capire già durante la compilazione quale metodo usare.

✔️ Quindi sì: l'overloading è polimorfismo
❌ Ma non è polimorfismo “dinamico” (quello che avviene a runtime con override)

👉 È invece polimorfismo statico, perché tutto viene deciso prima dell’esecuzione, in fase di compilazione.

Questo ti mostra quanto anche concetti “semplici” come l’overloading abbiano radici profonde nella programmazione orientata agli oggetti.

Capire queste connessioni ti aiuta a pensare da vero architetto software, anche nei dettagli.

Nel prossimo capitolo vedremo un’altra grande scelta: quando usare metodi statici e quando invece affidarsi ai metodi d’istanza. La risposta cambia tutto, soprattutto se stai costruendo classi ben progettate.

Metodi statici vs metodi d’istanza: quando usare cosa

Uno dei dilemmi più frequenti per chi inizia (ma anche per molti sviluppatori esperti) è:

👉 Devo rendere questo metodo statico oppure no?

È una domanda apparentemente semplice, ma la risposta ha implicazioni profonde sulla struttura e l’architettura del tuo codice.

🔹 Cosa significa “statico”

Un metodo statico appartiene direttamente alla classe, non ha bisogno di un oggetto per essere eseguito.

👉 Ti ricordi la differenza tra classe e oggetto? La classe è come un progetto, mentre l’oggetto è il risultato concreto di quel progetto.

Ecco perché un metodo statico si usa quando non hai bisogno di creare un oggetto, ma vuoi eseguire un’azione direttamente sul “tipo” stesso.

Puoi pensarlo come a un comportamento "globale" del tipo, valido sempre, senza dipendere da uno stato interno.

class Calcolatrice
    {
        public static int Somma(int a, int b)
        {
            return a + b;
        }
    }

    int risultato = Calcolatrice.Somma(2, 3); // 5

✅ È utile quando il metodo:

👤 Metodi d’istanza

Un metodo d’istanza richiede un oggetto della classe per poter essere invocato.

Significa che dipende da uno stato interno, come proprietà o campi della classe.

👉 In altre parole, il metodo ha bisogno di sapere “a chi” appartiene: deve lavorare su un oggetto preciso, con i suoi dati specifici.

Senza un oggetto, non potrebbe fare il suo lavoro, perché non saprebbe dove leggere o modificare le informazioni.

class Persona
    {
        public string Nome;

        public void Saluta()
        {
            Console.WriteLine($"Ciao, sono {Nome}");
        }
    }

    Persona p = new Persona { Nome = "Laura" };
    p.Saluta();

📌 Qui Saluta() usa Nome, che è un campo dell’istanza. Quindi ha senso che sia un metodo d’istanza.

🧠 Come scegliere?

Fatti queste domande:

📦 In sintesi:

🧠 Ricorda: se un metodo può essere statico... spesso dovrebbe esserlo.
Ma non forzare. La chiarezza e la coerenza vengono prima di tutto.

Nel prossimo capitolo concluderemo con una riflessione più ampia su come i metodi influenzano lo stile e la qualità del tuo codice. Preparati, perché inizierai a pensare da vero architetto.

La riflessione conclusiva su come i metodi definiscono lo stile e la qualità del codice

Se hai seguito questo articolo fino a qui, una cosa ti sarà ormai chiara:

👉 Un metodo non è solo una funzione. È una dichiarazione di intenti.

Racchiude un’idea, un comportamento, un pezzo di logica. E il modo in cui scegli di scriverlo racconta molto di te come sviluppatore:

I metodi sono il primo punto in cui la teoria incontra la pratica.
Sono il luogo dove le buone intenzioni si trasformano (o si perdono) nel codice vero.

Ma per vedere davvero il quadro completo, non puoi fermarti solo ai metodi.
Ogni metodo vive all’interno di una classe, e ogni classe modella un concetto, un'entità, un comportamento del mondo reale o astratto.

👉 Se vuoi consolidare questa visione e capire meglio il legame tra metodi e oggetti, ti consiglio di leggere anche questo articolo su classi e oggetti in C#. È il tassello che completa la prospettiva di insieme che ogni sviluppatore solido dovrebbe avere.

Scrivere metodi efficaci non significa solo farli funzionare, ma anche renderli leggibili, mantenibili e coerenti con l’intero progetto.

Per aiutarti a verificare se sei sulla buona strada ogni volta che ne scrivi uno, ti lascio una breve checklist a cui rispondere.

Farsi le domande giuste è quello che fa la differenza, sempre!

✅ Checklist: il tuo metodo è scritto bene?

Le risposte a queste domande ti aiuteranno a scrivere codice più:

E questo è il vero obiettivo di ogni sviluppatore che punta a fare il salto verso la progettazione.

Saper usare i metodi bene non è un dettaglio. È una skill fondamentale.

Non solo per far funzionare il codice, ma per farlo funzionare bene, a lungo e in squadra.

Quindi torna su queste nozioni ogni volta che scrivi.
Fermati, chiediti “sto facendo la scelta giusta?”

Perché ogni metodo è una scelta.
E la somma di tante scelte ben fatte è ciò che distingue chi scrive codice da chi costruisce software.


Prima di andare avanti o concludere, chiediti:

Se hai risposto "no" anche solo a una di queste domande... allora hai trovato l'articolo giusto da tenere nei preferiti e tornare a leggere ogni volta che ne avrai bisogno.

📘 Nel prossimo articolo parleremo di come organizzare i metodi all’interno delle classi per scrivere codice modulare, leggibile e pronto per crescere nel tempo. Non perdertelo!

A presto, nel prossimo capitolo del viaggio.

← Torna indietro