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.

📚 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

1
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!

2
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!

3
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

4
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 bool per indicare successo/fallimento
  • Usa out per 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.

1
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
}

2
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
    }
}

3
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) → double
  • CalcolaAreaCerchio(raggio) → double (usa π = 3.14)
  • CalcolaPerimetroRettangolo(base, altezza) → double
  • Overload di CalcolaArea che accetti diversi parametri
  • Metodo TryCalcolaArea con validazione parametri (out)

📝 Esercizio 2: Validatore Password

Crea una classe PasswordValidator con metodi per validare password:

  • ValidaLunghezza(password, minLength) → bool
  • ContieneMaiuscola(password) → bool
  • ContieneNumero(password) → bool
  • ValidaPassword(password, out string messaggio) → bool (verifica tutte le regole)
  • Overload di ValidaPassword con requisiti personalizzabili

🎯 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

Lezione 11 del Corso C# Completo

Torna in alto