Metodi e Valori di Ritorno in C#

Restituire un Risultato da un Metodo

Fino ad ora abbiamo visto esempi in cui il metodo si limita a stampare qualcosa sulla console. Tuttavia, i metodi non eseguono solitamente una semplice sequenza di istruzioni, ma restituiscono anche dei risultati.

Vediamo come dichiarare un metodo che restituisce un valore:

static tipo_di_ritorno nome_metodo(parametri)
{
    // Codice del metodo
    return risultato;
}

In precedenza abbiamo sempre usato void al posto di tipo_di_ritorno. Ora estendiamo questa definizione, poiché vedremo che void non è l'unica scelta. Invece di void possiamo restituire qualsiasi tipo, sia esso un tipo primitivo (come int, float, double, ecc.) oppure un tipo per riferimento (come string o array), a seconda del risultato che il metodo deve produrre.

Ad esempio, consideriamo un metodo che calcola l'area di un quadrato e, invece di stamparla, la restituisce:

static double AreaQuadrato(double lunghezzaLato)
{
    // Calcola e restituisce l'area del quadrato
}

Come si può notare, il valore restituito (l'area) è di tipo double.

Come Utilizzare il Valore Restituito?

Quando un metodo viene eseguito e restituisce un valore, C# "sostituisce" l'invocazione del metodo con il valore restituito, permettendoci di utilizzarlo per qualsiasi scopo. Vediamo alcuni esempi.

Assegnazione a una Variabile

Possiamo assegnare il risultato dell'esecuzione di un metodo a una variabile del tipo appropriato:

// Area del quadrato con lato di 5
double area = AreaQuadrato(5);

Utilizzo nelle Espressioni

Il valore restituito può essere utilizzato direttamente nelle espressioni. Ad esempio, per calcolare il prezzo totale di una fattura possiamo moltiplicare il prezzo unitario per la quantità:

float prezzoTotale = PrezzoSingolo() * quantita;

Utilizzo come Parametro di un Altro Metodo

Possiamo passare il risultato di un metodo direttamente come parametro di un'altra funzione:

Console.WriteLine(OttieniMessaggio());

In questo caso, il metodo OttieniMessaggio() viene invocato e il suo risultato, ad esempio "Ciao, Mondo!", sostituisce la chiamata nel parametro di WriteLine(), ottenendo l'effetto:

Console.WriteLine("Ciao, Mondo!");

Il Tipo di Valore Restituito

Come già accennato, il risultato restituito da un metodo può essere di qualsiasi tipo (int, string, array, ecc.). Tuttavia, se si usa la parola chiave void al posto di un tipo, significa che il metodo non restituisce alcun valore.

Definizione

Tipo del Valore Restituito

Per specificare il tipo di ritorno di un metodo basta sostituire void con il tipo di dato che il metodo restituirà. La sintassi è la seguente:

tipo_ritorno NomeMetodo(parametri)

Se il metodo non restituisce alcun valore, si usa void:

void NomeMetodo(parametri)

L'Operatore return

Per far sì che un metodo restituisca un valore, la parola chiave return deve essere seguita da un'espressione il cui risultato verrà restituito:

static tipo_di_ritorno NomeMetodo(parametri)
{
    // Codice che prepara il risultato
    return risultato;
}

Ad esempio:

static long Moltiplica(int numero1, int numero2)
{
    long risultato = numero1 * numero2;
    return risultato;
}

In questo metodo, dopo la moltiplicazione, viene restituita la variabile risultato.

Una cosa importante da notare è che, una volta che l'istruzione return viene eseguita, il metodo termina immediatamente e il controllo viene restituito al metodo chiamante.

Ad esempio, nel seguente metodo, se numero è minore di 0, il metodo termina immediatamente e restituisce -1:

static int CalcolaRadiceQuadrata(int numero)
{
    if (numero < 0)
    {
        return -1;
    }
    // Calcola la radice quadrata
}
Definizione

Valore Restituito

Per restituire un valore da un metodo, si usa l'istruzione return seguita dal valore da restituire. L'istruzione return termina immediatamente l'esecuzione del metodo e restituisce il controllo al metodo chiamante.

La sintassi è la seguente:

return espressione;
Consiglio

Valore Restituito e void

Se un metodo ha tipo void, non è necessario usare return. Tuttavia, se viene usato, deve essere seguito da un punto e virgola:

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

Compatibilità tra il Risultato e il Tipo di Ritorno

Il valore restituito dal metodo deve essere compatibile (o convertibile implicitamente) con il tipo dichiarato. Ad esempio, il seguente codice è valido:

static float Moltiplica(int numero1, int numero2)
{
    int risultato = numero1 * numero2;
    return risultato;
}

Anche se risultato è di tipo int, esso viene convertito implicitamente in float.

Utilizzo di un'Espressione dopo return

È possibile inserire direttamente un'espressione dopo return:

static int Moltiplica(int numero1, int numero2)
{
    return numero1 * numero2;
}

Qui, il risultato dell'espressione numero1 * numero2 viene restituito immediatamente.

Caratteristiche dell'Operatore return

L'istruzione return fa due cose:

  • Interrompe immediatamente l'esecuzione del metodo.
  • Restituisce il risultato al metodo chiamante.

Pertanto, non è corretto inserire codice dopo un return, perché quel codice non verrà mai eseguito. Ad esempio:

static int Somma(int numero1, int numero2)
{
    int risultato = numero1 + numero2;
    return risultato;
    // Il seguente codice non verrà mai eseguito:
    risultato = 0;
}

Quando il metodo ha tipo void, return viene usato semplicemente per interrompere l'esecuzione:

static void StampaNumeroPositivo(int numero)
{
    if (numero <= 0)
    {
        // Se il numero NON è positivo, termina il metodo
        return;
    }
    Console.WriteLine(numero);
}

Molteplici Istruzioni return

È possibile avere più istruzioni return in un metodo, purché almeno una di esse venga eseguita. Ad esempio:

static int Compara(int numero1, int numero2)
{
    if (numero1 > numero2)
    {
        return 1;
    }
    else if (numero1 == numero2)
    {
        return 0;
    }
    else
    {
        return -1;
    }
}

Usare più return è comune soprattutto quando il metodo gestisce diversi casi.

Perché il Tipo di Valore Restituito non Fa Parte della Firma del Metodo?

Nella lezione precedente abbiamo studiato l'overload dei metodi e abbiamo visto che possiamo realizzare metodi con lo stesso nome ma con firme diverse.

Tuttavia, abbiamo anticipato che in C# non è possibile avere metodi con lo stesso nome e lista di parametri ma con tipi di ritorno diversi. Ad esempio, il seguente codice non verrà compilato:

static int Somma(int numero1, int numero2)
{
    return (numero1 + numero2);
}

static double Somma(int numero1, int numero2)
{
    return (numero1 + numero2);
}

Il compilatore non riesce a distinguere tra i due metodi perché la firma (nome e parametri) è identica; il tipo di ritorno non viene considerato nella firma del metodo.

Esempio: Conversione da Fahrenheit a Celsius

Supponiamo di dover scrivere un programma che, data una temperatura corporea espressa in gradi Fahrenheit (inserita dall'utente), la converta in gradi Celsius e stampi il messaggio:

La tua temperatura corporea in gradi Celsius è X

dove X rappresenta il valore convertito. Inoltre, se la temperatura in Celsius è pari o superiore a 37 gradi, il programma deve stampare il messaggio:

Hai la febbre!

La formula per convertire Fahrenheit in Celsius è:

°C = (°F - 32) \cdot \frac{5}{9}

Analizzando il problema, possiamo suddividerlo nei seguenti passaggi:

  • Acquisire in input la temperatura in gradi Fahrenheit.
  • Convertire il valore in Celsius.
  • Stampare il messaggio con il risultato.
  • Se la temperatura in Celsius è ≥ 37, avvisare l'utente che ha la febbre.

Un'implementazione di esempio è la seguente:

using System;

class ConversioneTemperatura
{
    static double ConvertiFahrenheitACelsius(double temperaturaF)
    {
        double temperaturaC = (temperaturaF - 32) * 5 / 9;
        return temperaturaC;
    }

    static void Main()
    {
        Console.Write("Inserisci la tua temperatura corporea ");
        Console.Write("in gradi Fahrenheit: ");

        double temperatura = double.Parse(Console.ReadLine());

        temperatura = ConvertiFahrenheitACelsius(temperatura);

        Console.Write("La tua temperatura corporea ");
        Console.WriteLine("in gradi Celsius è {0}.", temperatura);

        if (temperatura >= 37)
        {
            Console.WriteLine("Hai la Febbre!");
        }
    }
}

In questo esempio:

  • Viene chiesto all'utente di inserire la temperatura in Fahrenheit.
  • Il metodo ConvertiFahrenheitACelsius converte la temperatura in Celsius.
  • Il risultato viene stampato e, se necessario, viene visualizzato un avviso.

Esempio: Differenza tra Due Mesi

Consideriamo il seguente compito: scrivere un programma che, dati due numeri compresi tra 1 e 12 (che rappresentano dei mesi), calcoli il numero di mesi compresi tra essi e stampi il messaggio:

C'è un periodo di X mesi da Y a Z.

dove X è il numero di mesi, Y è il nome del mese di inizio e Z quello di fine.

I passaggi per risolvere il problema sono:

  • Acquisire in input i numeri che rappresentano i mesi.
  • Calcolare la differenza (tenendo conto che, se il secondo mese è minore del primo, si intende il mese del prossimo anno).
  • Stampare il messaggio sostituendo i numeri con i relativi nomi dei mesi.

Ecco una possibile implementazione:

using System;

class Mesi
{
    static string NomeMese(int mese)
    {
        string nomeMese;
        switch (mese)
        {
            case 1:
                nomeMese = "Gennaio";
                break;
            case 2:
                nomeMese = "Febbraio";
                break;
            case 3:
                nomeMese = "Marzo";
                break;
            case 4:
                nomeMese = "Aprile";
                break;
            case 5:
                nomeMese = "Maggio";
                break;
            case 6:
                nomeMese = "Giugno";
                break;
            case 7:
                nomeMese = "Luglio";
                break;
            case 8:
                nomeMese = "Agosto";
                break;
            case 9:
                nomeMese = "Settembre";
                break;
            case 10:
                nomeMese = "Ottobre";
                break;
            case 11:
                nomeMese = "Novembre";
                break;
            case 12:
                nomeMese = "Dicembre";
                break;
            default:
                Console.WriteLine("Mese non valido!");
                return null;
        }
        return nomeMese;
    }

    static void StampaPeriodo(int meseInizio, int meseFine)
    {
        int periodo = meseFine - meseInizio;

        if (periodo < 0)
        {
            // Gestione della distanza negativa: se il secondo mese è minore,
            // si assume che appartenga all'anno successivo
            periodo = periodo + 12;
        }

        Console.WriteLine("C'è un periodo di {0} mesi da {1} a {2}.",
                            periodo,
                            NomeMese(meseInizio),
                            NomeMese(meseFine));
    }

    static void Main()
    {
        Console.Write("Primo Mese (1-12): ");
        int primoMese = int.Parse(Console.ReadLine());

        Console.Write("Secondo mese (1-12): ");
        int secondoMese = int.Parse(Console.ReadLine());

        SayPeriod(primoMese, secondoMese);
    }

}

Nel metodo NomeMese utilizziamo una struttura switch per associare il numero del mese al relativo nome. Se il numero non è compreso tra 1 e 12, viene visualizzato un messaggio di errore.

Il metodo StampaPeriodo calcola la differenza fra i mesi, correggendo eventuali differenze negative (aggiungendo 12) e stampa il messaggio formattato.

Un possibile output, se l'input fosse 2 e 6, sarebbe:

Primo Mese (1-12): 2
Secondo mese (1-12): 6
C'è un periodo di 4 mesi da Febbraio a Giugno.

Esempio: Validazione dei Dati di Input

In questo esempio scriviamo un programma che chiede all'utente l'orario corrente.

Il programma visualizza il messaggio:

Che ora è?

L'utente deve quindi inserire due numeri: uno per le ore e uno per i minuti. Se l'orario inserito è valido, viene stampato il messaggio:

L'ora è hh:mm adesso.

Altrimenti, se i dati non sono corretti, il programma visualizza:

Ora non corretta!

Il problema viene scomposto in questi passaggi:

  • Acquisire i dati per ore e minuti.
  • Validare che le ore siano nell'intervallo 0–23 e i minuti nell'intervallo 0–59.
  • Stampare il messaggio corrispondente.

Per gestire la validazione, creiamo due metodi separati: uno per le ore e uno per i minuti.

Ecco la soluzione:

using System;

class Validazione
{

    static void Main()
    {
        Console.WriteLine("Che ora è?");

        Console.Write("Ore: ");
        int ore = int.Parse(Console.ReadLine());

        Console.Write("Minuti: ");
        int minuti = int.Parse(Console.ReadLine());

        bool oraValida = ValidaOre(ore) && ValidaMinuti(minuti);
        if (oraValida)
        {
            Console.WriteLine("L'ora è {0}:{1} adesso.", ore, minuti);
        }
        else
        {
            Console.WriteLine("Ora non corretta!");
        }
    }

    static bool ValidaOre(int ore)
    {
        bool risultato = (ore >= 0) && (ore < 24);
        return risultato;
    }

    static bool ValidaMinuti(int minuti)
    {
        bool risultato = (minuti >= 0) && (minuti <= 59);
        return risultato;
    }

}

Il metodo ValidaOre controlla se il valore delle ore è compreso tra 0 e 23, mentre ValidaMinuti verifica se i minuti sono compresi tra 0 e 59. Nel metodo Main:

  • Vengono chiesti all'utente i dati di input.
  • I dati vengono validati combinando il risultato dei due metodi con l'operatore logico &&.
  • Viene stampato il messaggio corretto in base alla validità dell'orario.

Un possibile output, con dati corretti, potrebbe essere:

Che ora è?
Ore: 17
Minuti: 33
L'ora è 17:33 adesso.

Se i dati inseriti non sono validi, ad esempio:

Che ora è?
Ore: 33
Minuti: -2
Ora non corretta!

In questo caso, i minuti sono negativi, quindi l'orario non è valido.

Conclusione

In questa lezione abbiamo imparato a restituire valori da un metodo in C#.

In particolare, abbiamo visto che:

  • Un metodo può restituire un valore di qualsiasi tipo, non solo void.
  • Il valore restituito può essere utilizzato in molteplici modi, come assegnarlo a una variabile, passarlo come parametro o utilizzarlo in espressioni.
  • L'istruzione return è usata per restituire un valore da un metodo.
  • Il tipo del valore restituito deve essere compatibile con il tipo dichiarato.
  • È possibile avere più istruzioni return in un metodo, ma solo una verrà eseguita.
  • Il tipo di ritorno di un metodo non fa parte della firma del metodo.

Abbiamo anche visto alcuni esempi pratici che illustrano come restituire valori da un metodo e come utilizzarli in situazioni reali.