Organizzare il codice in blocchi riutilizzabili per programmi più puliti e manutenibili.
🖥️ In pratica con Windows Forms: Ripetere e riutilizzare: cicli e metodi.
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.
📚 Metodi vs Funzioni
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.
✨ Vantaggi dei Metodi
Riutilizzo del codice
Scrivi una volta, usa ovunque
Leggibilità
Codice organizzato e comprensibile
Manutenibilità
Modifiche in un solo punto
Testing
Facile testare singole funzionalità
Definizione di un Metodo
La sintassi base per definire un metodo in C# segue una struttura precisa con diversi componenti.
Sintassi Base Metodo
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; |
Esempio Annotato
// ↓ 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
- Metodo Void - Senza Parametri e Senza Ritorno
Esegue un'azione ma non restituisce nulla e non accetta input.
Metodo Void Base
public void SalutaUtente()
{
Console.WriteLine("Benvenuto al programma!");
}
// Chiamata
SalutaUtente(); // Output: Benvenuto al programma!
- Metodo Void - Con Parametri ma Senza Ritorno
Accetta input ma non restituisce valori.
Metodo con Parametri
public void SalutaPersona(string nome)
{
Console.WriteLine($"Ciao {nome}!");
}
// Chiamate
SalutaPersona("Marco"); // Output: Ciao Marco!
SalutaPersona("Anna"); // Output: Ciao Anna!
- Metodo con Parametri e Valore di Ritorno
Accetta input e restituisce un risultato.
Metodo con Return
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
- Metodo con Parametri Opzionali
Parametri con valori predefiniti che possono essere omessi.
Parametri Opzionali
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
📌 Regole Parametri Opzionali
- 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.
Passaggio per Valore
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.
Passaggio per Riferimento
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!)
⚠️ IMPORTANTE: ref richiede inizializzazione
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.
Parametri out
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");
}
💡 Pattern Try-
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
📊 Confronto: Valore vs ref vs out
| 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).
Esempio Overloading
// 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;
}
Utilizzo Overloading
// 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
📌 Regole per l'Overloading
- 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
💡 params - Numero Variabile di Parametri
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.
- Variabili Locali
Dichiarate dentro un metodo, visibili solo in quel metodo.
Variabili Locali
public void Metodo1()
{
int x = 5; // Variabile locale
Console.WriteLine(x); // ✅ OK
}
public void Metodo2()
{
Console.WriteLine(x); // ❌ ERRORE: x non esiste qui
}
- Variabili di Classe (Campi)
Dichiarate a livello di classe, visibili in tutti i metodi della classe.
Campi di 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
}
}
- Variabili di Blocco
Dichiarate dentro un blocco (if, for, while), visibili solo in quel blocco.
Variabili di 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
}
🎯 Regole dello Scope
- 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
Esempio Completo Scope
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.
Calcolatrice.cs - Classe Completa
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);
}
}
💡 Analisi dell'Architettura
- 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)
Mostra la soluzione
Soluzione Esercizio 1
using System;
class Geometria
{
private const double PI = 3.14159;
// Area rettangolo
public double CalcolaAreaRettangolo(double baseRett, double altezza)
{
return baseRett * altezza;
}
// Area cerchio
public double CalcolaAreaCerchio(double raggio)
{
return PI * raggio * raggio;
}
// Perimetro rettangolo
public double CalcolaPerimetroRettangolo(double baseRett, double altezza)
{
return 2 * (baseRett + altezza);
}
// Overload - Area quadrato (un solo parametro)
public double CalcolaArea(double lato)
{
return lato * lato;
}
// Overload - Area rettangolo
public double CalcolaArea(double baseRett, double altezza)
{
return baseRett * altezza;
}
// Overload - Area triangolo (3 parametri)
public double CalcolaArea(double a, double b, double c)
{
// Formula di Erone
double s = (a + b + c) / 2;
return Math.Sqrt(s * (s - a) * (s - b) * (s - c));
}
// Metodo con validazione
public bool TryCalcolaAreaRettangolo(double baseRett, double altezza, out double area)
{
area = 0;
if (baseRett <= 0 || altezza <= 0)
{
return false;
}
area = baseRett * altezza;
return true;
}
}
class Program
{
static void Main()
{
Geometria geo = new Geometria();
// Test calcoli
Console.WriteLine($"Area rettangolo 5x3: {geo.CalcolaAreaRettangolo(5, 3)}");
Console.WriteLine($"Area cerchio r=4: {geo.CalcolaAreaCerchio(4):F2}");
Console.WriteLine($"Perimetro rett 5x3: {geo.CalcolaPerimetroRettangolo(5, 3)}");
// Test overload
Console.WriteLine($"\nArea quadrato lato 5: {geo.CalcolaArea(5)}");
Console.WriteLine($"Area rettangolo 4x6: {geo.CalcolaArea(4, 6)}");
Console.WriteLine($"Area triangolo 3,4,5: {geo.CalcolaArea(3, 4, 5):F2}");
// Test validazione
if (geo.TryCalcolaAreaRettangolo(5, 3, out double area))
{
Console.WriteLine($"\nArea valida: {area}");
}
if (!geo.TryCalcolaAreaRettangolo(-5, 3, out area))
{
Console.WriteLine("Parametri non validi!");
}
}
}
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
Mostra la soluzione
Soluzione Esercizio 2
using System;
class PasswordValidator
{
// Valida lunghezza minima
public bool ValidaLunghezza(string password, int minLength)
{
return password.Length >= minLength;
}
// Verifica presenza maiuscola
public bool ContieneMaiuscola(string password)
{
foreach (char c in password)
{
if (char.IsUpper(c))
{
return true;
}
}
return false;
}
// Verifica presenza numero
public bool ContieneNumero(string password)
{
foreach (char c in password)
{
if (char.IsDigit(c))
{
return true;
}
}
return false;
}
// Validazione completa con messaggio
public bool ValidaPassword(string password, out string messaggio)
{
messaggio = "";
if (!ValidaLunghezza(password, 8))
{
messaggio = "Password troppo corta (min 8 caratteri)";
return false;
}
if (!ContieneMaiuscola(password))
{
messaggio = "Password deve contenere almeno una maiuscola";
return false;
}
if (!ContieneNumero(password))
{
messaggio = "Password deve contenere almeno un numero";
return false;
}
messaggio = "Password valida!";
return true;
}
// Overload con requisiti personalizzati
public bool ValidaPassword(string password, int minLength,
bool richiedeMaiuscola, bool richiede Numero,
out string messaggio)
{
messaggio = "";
if (!ValidaLunghezza(password, minLength))
{
messaggio = $"Password troppo corta (min {minLength} caratteri)";
return false;
}
if (richiedeMaiuscola && !ContieneMaiuscola(password))
{
messaggio = "Password deve contenere maiuscola";
return false;
}
if (richiede Numero && !ContieneNumero(password))
{
messaggio = "Password deve contenere numero";
return false;
}
messaggio = "Password valida!";
return true;
}
}
class Program
{
static void Main()
{
PasswordValidator validator = new PasswordValidator();
string[] passwords = { "ciao", "Ciao1234", "password", "MyPass99" };
Console.WriteLine("=== TEST VALIDAZIONE PASSWORD ===\n");
foreach (string pwd in passwords)
{
if (validator.ValidaPassword(pwd, out string msg))
{
Console.WriteLine($"✅ '{pwd}': {msg}");
}
else
{
Console.WriteLine($"❌ '{pwd}': {msg}");
}
}
// Test overload con requisiti custom
Console.WriteLine("\n=== VALIDAZIONE CUSTOM (min 6, no maiuscola) ===");
if (validator.ValidaPassword("pass123", 6, false, true, out string msgCustom))
{
Console.WriteLine($"✅ {msgCustom}");
}
}
}
🎯 Riepilogo Lezione
Concetti Fondamentali:
- Definizione metodi
- Parametri e return
- void vs tipo ritorno
- Parametri opzionali
- Chiamata metodi
Concetti Avanzati:
- Passaggio per valore/riferimento
- ref e out parameters
- Overloading metodi
- params (varargs)
- Scope variabili