Funzioni e Metodi in C#
Organizzare il codice in blocchi riutilizzabili per programmi più puliti e manutenibili
🎯 Introduzione ai Metodi
I metodi (o funzioni) sono blocchi di codice che eseguono specifiche operazioni
e possono essere chiamati da altre parti del programma. Sono fondamentali per applicare il principio
DRY (Don’t Repeat Yourself), consentendo di riutilizzare lo stesso codice più volte
senza doverlo riscrivere.
💡 Perché Usare i Metodi?
Immagina di dover calcolare l’area di un rettangolo in 10 punti diversi del tuo programma.
Senza metodi, dovresti scrivere base * altezza 10 volte! Con un metodo
CalcolaArea(base, altezza), lo scrivi una volta e lo richiami quando serve.
In C#, poiché è un linguaggio orientato agli oggetti, si parla principalmente
di “metodi”, che sono funzioni che appartengono a una classe. Tuttavia, i concetti di base
sono gli stessi delle funzioni in altri linguaggi come JavaScript o Python.
Scrivi una volta, usa ovunque
Codice organizzato e comprensibile
Modifiche in un solo punto
Facile testare singole funzionalità
🔧 Definizione di un Metodo
La sintassi base per definire un metodo in C# segue una struttura precisa con diversi componenti.
modificatoreAccesso tipoDiRitorno NomeMetodo(parametri)
{
// corpo del metodo
return valoreDiRitorno; // se il tipo di ritorno non è void
}
Componenti di un Metodo
| Componente | Descrizione | Esempio |
|---|---|---|
| Modificatore accesso | Determina la visibilità del metodo | public, private |
| Tipo di ritorno | Tipo di dato restituito (o void) |
int, string, void |
| Nome metodo | Identificatore, verbo in PascalCase | CalcolaSomma, GetNome |
| Parametri | Input del metodo (opzionali) | (int a, int b) |
| Corpo | Codice eseguito alla chiamata | { return a + b; } |
| Return | Restituisce valore (se non void) | return risultato; |
// ↓ Modificatore ↓ Tipo ritorno ↓ Nome ↓ Parametri
public int CalcolaSomma(int a, int b)
{
// ↓ Corpo del metodo
int risultato = a + b;
// ↓ Return (restituisce il valore)
return risultato;
}
📝 Tipologie di Metodi
Esegue un’azione ma non restituisce nulla e non accetta input.
public void SalutaUtente()
{
Console.WriteLine("Benvenuto al programma!");
}
// Chiamata
SalutaUtente(); // Output: Benvenuto al programma!
Accetta input ma non restituisce valori.
public void SalutaPersona(string nome)
{
Console.WriteLine($"Ciao {nome}!");
}
// Chiamate
SalutaPersona("Marco"); // Output: Ciao Marco!
SalutaPersona("Anna"); // Output: Ciao Anna!
Accetta input e restituisce un risultato.
public int Somma(int a, int b)
{
return a + b;
}
// Utilizzo
int risultato = Somma(5, 3);
Console.WriteLine(risultato); // Output: 8
// Oppure direttamente
Console.WriteLine(Somma(10, 20)); // Output: 30
Parametri con valori predefiniti che possono essere omessi.
public void ConfiguraUtente(string nome, int età = 18, bool isAdmin = false)
{
Console.WriteLine($"Utente: {nome}, Età: {età}, Admin: {isAdmin}");
}
// Chiamate diverse - tutti validi!
ConfiguraUtente("Mario", 25, true);
// Output: Utente: Mario, Età: 25, Admin: True
ConfiguraUtente("Luigi", 30);
// Output: Utente: Luigi, Età: 30, Admin: False
ConfiguraUtente("Peach");
// Output: Utente: Peach, Età: 18, Admin: False
- I parametri opzionali devono essere dopo quelli obbligatori
- Il valore di default viene assegnato con
= - Puoi omettere i parametri opzionali dalla fine
- Esempio:
Method(string nome, int età = 18)✅ - Errore:
Method(int età = 18, string nome)❌
🔄 Passaggio Parametri: Valore vs Riferimento
In C# esistono due modi principali per passare parametri ai metodi: per valore
(default) e per riferimento.
Passaggio per Valore (Default)
Viene passata una copia del valore. Le modifiche al parametro nel metodo
NON influenzano la variabile originale.
public void IncrementaNumero(int numero)
{
numero += 10; // Modifica solo la copia locale
Console.WriteLine($"Dentro il metodo: {numero}");
}
int x = 5;
IncrementaNumero(x);
// Output dentro metodo: 15
Console.WriteLine($"Fuori dal metodo: {x}");
// Output fuori: 5 (INVARIATO!)
Passaggio per Riferimento (ref)
Viene passato un riferimento alla variabile originale. Le modifiche
influenzano la variabile originale.
public void IncrementaNumero(ref int numero)
{
numero += 10; // Modifica la variabile originale
Console.WriteLine($"Dentro il metodo: {numero}");
}
int x = 5;
IncrementaNumero(ref x); // ⚠️ Serve 'ref' anche nella chiamata!
// Output dentro metodo: 15
Console.WriteLine($"Fuori dal metodo: {x}");
// Output fuori: 15 (MODIFICATO!)
Quando usi ref, la variabile DEVE essere inizializzata prima della chiamata:
// ❌ ERRORE int y; // Non inizializzata IncrementaNumero(ref y); // ERRORE! // ✅ CORRETTO int y = 0; // Inizializzata IncrementaNumero(ref y); // OK
Parametri di Output (out)
Il modificatore out permette a un metodo di restituire più valori.
A differenza di ref, la variabile NON deve essere inizializzata prima.
public bool TryDividi(int a, int b, out double risultato)
{
if (b == 0)
{
risultato = 0; // Deve essere assegnato prima del return
return false; // Indica fallimento
}
risultato = (double)a / b;
return true; // Indica successo
}
// Utilizzo - la variabile NON deve essere inizializzata
if (TryDividi(10, 2, out double ris))
{
Console.WriteLine($"Risultato: {ris}"); // Output: Risultato: 5
}
else
{
Console.WriteLine("Divisione non valida");
}
Il pattern Try... con out è molto comune in C# per operazioni
che possono fallire. Esempi: int.TryParse(), Dictionary.TryGetValue().
- Restituisce
boolper indicare successo/fallimento - Usa
outper restituire il valore effettivo - Evita eccezioni per situazioni comuni
| Caratteristica | Valore (default) | ref | out |
|---|---|---|---|
| Inizializzazione | Non necessaria | Obbligatoria | Non necessaria |
| Modifica originale | ❌ No | ✅ Sì | ✅ Sì |
| Assegnazione nel metodo | Opzionale | Opzionale | Obbligatoria |
| Uso principale | Input normale | Modificare input | Restituire valori extra |
🔀 Overloading dei Metodi
L’overloading (sovraccarico) permette di definire più metodi con lo
stesso nome ma con parametri diversi (numero o tipo).
// Versione 1: due int
public int Somma(int a, int b)
{
return a + b;
}
// Versione 2: due double
public double Somma(double a, double b)
{
return a + b;
}
// Versione 3: tre int
public int Somma(int a, int b, int c)
{
return a + b + c;
}
// Versione 4: array di int
public int Somma(params int[] numeri)
{
int totale = 0;
foreach (int num in numeri)
{
totale += num;
}
return totale;
}
// Il compilatore sceglie automaticamente la versione corretta! int ris1 = Somma(5, 3); // Chiama versione 1 → 8 double ris2 = Somma(2.5, 3.5); // Chiama versione 2 → 6.0 int ris3 = Somma(1, 2, 3); // Chiama versione 3 → 6 int ris4 = Somma(1, 2, 3, 4, 5); // Chiama versione 4 → 15
- Il nome deve essere identico
- I parametri devono differire per:
- Numero di parametri
- Tipo di parametri
- Ordine dei parametri
- Il tipo di ritorno da solo NON basta per l’overloading
- Utile per creare API flessibili e intuitive
La keyword params permette di passare un numero variabile di argomenti
dello stesso tipo. Deve essere l’ultimo parametro.
public void StampaNumeri(params int[] numeri)
{
foreach (int num in numeri)
{
Console.WriteLine(num);
}
}
// Tutte queste chiamate sono valide!
StampaNumeri(1);
StampaNumeri(1, 2, 3);
StampaNumeri(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
🔍 Scope delle Variabili
Lo scope (ambito di visibilità) determina dove una variabile può essere
utilizzata nel codice. Dipende da dove viene dichiarata.
Dichiarate dentro un metodo, visibili solo in quel metodo.
public void Metodo1()
{
int x = 5; // Variabile locale
Console.WriteLine(x); // ✅ OK
}
public void Metodo2()
{
Console.WriteLine(x); // ❌ ERRORE: x non esiste qui
}
Dichiarate a livello di classe, visibili in tutti i metodi della classe.
class Esempio
{
// Campo di classe (field)
private int numero = 10;
public void Metodo1()
{
Console.WriteLine(numero); // ✅ OK - campo accessibile
numero = 20; // ✅ Posso modificarlo
}
public void Metodo2()
{
Console.WriteLine(numero); // ✅ OK - sempre accessibile
}
}
Dichiarate dentro un blocco (if, for, while), visibili solo in quel blocco.
public void Esempio()
{
int x = 5; // Variabile locale del metodo
if (x > 0)
{
int y = x * 2; // Variabile di blocco
Console.WriteLine(y); // ✅ OK qui
}
Console.WriteLine(y); // ❌ ERRORE: y non esiste fuori dal blocco
for (int i = 0; i < 5; i++)
{
int z = i * 2; // Variabile di blocco del for
}
Console.WriteLine(i); // ❌ ERRORE: i esiste solo nel for
Console.WriteLine(z); // ❌ ERRORE: z esiste solo nel for
}
- Le variabili sono visibili solo nel loro scope e in quelli interni
- Non puoi dichiarare due variabili con lo stesso nome nello stesso scope
- Una variabile locale può "nascondere" un campo di classe con lo stesso nome
- Le variabili di blocco vengono distrutte quando il blocco termina
class EsempioScope
{
// ① Campo di classe - visibile ovunque nella classe
private int campoDiClasse = 100;
public void MetodoEsempio()
{
// ② Variabile locale - visibile solo in questo metodo
int variabileLocale = 50;
Console.WriteLine(campoDiClasse); // ✅ OK
Console.WriteLine(variabileLocale); // ✅ OK
if (variabileLocale > 0)
{
// ③ Variabile di blocco - solo dentro l'if
int variabileBlocco = 25;
Console.WriteLine(campoDiClasse); // ✅ OK
Console.WriteLine(variabileLocale); // ✅ OK
Console.WriteLine(variabileBlocco); // ✅ OK
}
// Console.WriteLine(variabileBlocco); // ❌ ERRORE
}
public void AltroMetodo()
{
Console.WriteLine(campoDiClasse); // ✅ OK
// Console.WriteLine(variabileLocale); // ❌ ERRORE
}
}
🚀 Esempio Completo: Calcolatrice Avanzata
Vediamo un esempio completo che utilizza tutti i concetti appresi: metodi, overloading,
parametri out, e organizzazione del codice.
using System;
class Calcolatrice
{
// ===== OPERAZIONI BASE =====
public int Somma(int a, int b)
{
return a + b;
}
public double Somma(double a, double b) // Overloading
{
return a + b;
}
public int Sottrazione(int a, int b)
{
return a - b;
}
public int Moltiplicazione(int a, int b)
{
return a * b;
}
// Divisione con gestione errore
public double Divisione(int a, int b)
{
if (b == 0)
{
throw new DivideByZeroException("Impossibile dividere per zero");
}
return (double)a / b;
}
// ===== METODI AVANZATI =====
// Pattern Try- con out parameter
public bool TryDivisione(int a, int b, out double risultato)
{
if (b == 0)
{
risultato = 0;
return false;
}
risultato = (double)a / b;
return true;
}
// Metodo con params per numero variabile di parametri
public int SommaMultipla(params int[] numeri)
{
int totale = 0;
foreach (int num in numeri)
{
totale += num;
}
return totale;
}
// Metodo che determina quale operazione eseguire
public bool CalcolaRisultato(int a, int b, char operazione, out double risultato)
{
risultato = 0;
switch (operazione)
{
case '+':
risultato = Somma(a, b);
return true;
case '-':
risultato = Sottrazione(a, b);
return true;
case '*':
risultato = Moltiplicazione(a, b);
return true;
case '/':
return TryDivisione(a, b, out risultato);
default:
return false;
}
}
// Metodo helper per mostrare risultato
private void MostraRisultato(string operazione, double risultato)
{
Console.WriteLine($"Risultato {operazione}: {risultato:F2}");
}
// Metodo pubblico che usa il metodo privato
public void EseguiCalcolo(int a, int b, char op)
{
if (CalcolaRisultato(a, b, op, out double risultato))
{
MostraRisultato(op.ToString(), risultato);
}
else
{
Console.WriteLine("Operazione non valida!");
}
}
}
// ===== PROGRAMMA PRINCIPALE =====
class Program
{
static void Main()
{
Calcolatrice calc = new Calcolatrice();
Console.WriteLine("=== CALCOLATRICE C# ===\n");
// Test operazioni base
Console.WriteLine("--- Operazioni Base ---");
Console.WriteLine($"5 + 3 = {calc.Somma(5, 3)}");
Console.WriteLine($"10 - 4 = {calc.Sottrazione(10, 4)}");
Console.WriteLine($"6 * 7 = {calc.Moltiplicazione(6, 7)}");
// Test divisione sicura con TryDivisione
Console.WriteLine("\n--- Divisione Sicura ---");
if (calc.TryDivisione(10, 2, out double ris1))
{
Console.WriteLine($"10 / 2 = {ris1}");
}
if (!calc.TryDivisione(10, 0, out double ris2))
{
Console.WriteLine("10 / 0 = Errore (divisione per zero)");
}
// Test somma multipla (params)
Console.WriteLine("\n--- Somma Multipla ---");
Console.WriteLine($"Somma(1,2,3,4,5) = {calc.SommaMultipla(1, 2, 3, 4, 5)}");
// Test calcolatrice interattiva
Console.WriteLine("\n--- Calcolatrice Interattiva ---");
Console.Write("Primo numero: ");
int num1 = int.Parse(Console.ReadLine());
Console.Write("Secondo numero: ");
int num2 = int.Parse(Console.ReadLine());
Console.Write("Operazione (+, -, *, /): ");
char op = Console.ReadLine()[0];
calc.EseguiCalcolo(num1, num2, op);
}
}
- Separazione responsabilità - Classe Calcolatrice vs Program
- Overloading - Somma(int, int) e Somma(double, double)
- Pattern Try- - TryDivisione per evitare eccezioni
- params - SommaMultipla accetta numero variabile parametri
- out parameter - CalcolaRisultato restituisce bool + risultato
- Metodo privato - MostraRisultato (helper interno)
- Riutilizzo - EseguiCalcolo usa altri metodi della classe
💪 Esercizi Pratici
📝 Esercizio 1: Libreria Geometria
Crea una classe Geometria con i seguenti metodi:
CalcolaAreaRettangolo(base, altezza)→ doubleCalcolaAreaCerchio(raggio)→ double (usa π = 3.14)CalcolaPerimetroRettangolo(base, altezza)→ double- Overload di
CalcolaAreache accetti diversi parametri - Metodo
TryCalcolaAreacon validazione parametri (out)
📝 Esercizio 2: Validatore Password
Crea una classe PasswordValidator con metodi per validare password:
ValidaLunghezza(password, minLength)→ boolContieneMaiuscola(password)→ boolContieneNumero(password)→ boolValidaPassword(password, out string messaggio)→ bool (verifica tutte le regole)- Overload di
ValidaPasswordcon requisiti personalizzabili
- Definizione metodi
- Parametri e return
- void vs tipo ritorno
- Parametri opzionali
- Chiamata metodi
- Passaggio per valore/riferimento
- ref e out parameters
- Overloading metodi
- params (varargs)
- Scope variabili
Lezione 11 del Corso C# Completo