Vai al contenuto

Ripetere e riutilizzare: cicli e metodi

Far ripetere le istruzioni al programma con for, while e do-while, ricordare valori tra un clic e l'altro, e scrivere metodi propri con parametri e return per non copiare il codice.

Negli esercizi precedenti ogni clic eseguiva le istruzioni una volta sola. Ma come si stampa la tabellina del 7 senza scrivere dieci righe quasi uguali? E se lo stesso calcolo serve in tre bottoni diversi, bisogna copiarlo tre volte? No: per ripetere si usano i cicli, per riutilizzare si usano i metodi.

In questa lezione impari a:

  • ripetere istruzioni con for, while e do-while;
  • usare contatori e accumulatori (somme, conteggi, massimo);
  • far ricordare al form un valore tra un clic e l'altro;
  • scrivere metodi tuoi, con parametri e return.

Per mostrare tanti risultati useremo una TextBox su più righe: aggiungi una TextBox, chiamala txtOutput e nelle Proprietà imposta Multiline = True, ScrollBars = Vertical e ReadOnly = True. Poi allargala con i quadratini bianchi. Nella prossima lezione vedrai la ListBox, fatta apposta per gli elenchi.

Il ciclo for: ripetere un numero preciso di volte

Il for si usa quando sai quante volte ripetere. Ha tre parti, separate dal punto e virgola:

for (int i = 1; i <= 10; i++)
{
    // istruzioni ripetute
}
ParteEsempioQuando viene eseguita
inizioint i = 1una volta sola, prima di cominciare: crea il contatore
condizionei <= 10prima di ogni giro: se è vera si entra, se è falsa il ciclo finisce
passoi++alla fine di ogni giro: aumenta il contatore di 1

Esempio: la tabellina. Sul form ci sono txtNumero, btnTabellina e la TextBox txtOutput.

private void btnTabellina_Click(object sender, EventArgs e)
{
    int n;
    if (!int.TryParse(txtNumero.Text, out n))
    {
        MessageBox.Show("Scrivi un numero intero.");
        return;
    }

    txtOutput.Clear();                      // svuota l'output precedente

    for (int i = 1; i <= 10; i++)
    {
        txtOutput.AppendText(n + " x " + i + " = " + (n * i) + Environment.NewLine);
    }
}

AppendText aggiunge il testo in fondo alla TextBox; Environment.NewLine va a capo.

Prova: guarda il for giro per giro

Cambia i tre pezzi del for e premi Esegui: la tabella mostra, per ogni giro, quanto vale i, se la condizione è vera e cosa viene scritto. Prova anche a contare all'indietro (inizio 10, condizione i >= 1, passo i--) o di due in due (i += 2).

for (int i = ; i ; )
    txtOutput.AppendText("Giro " + i);

Contatori e accumulatori

Dentro un ciclo si usano spesso due tipi di variabili, sempre create prima del ciclo:

  • il contatore conta quante volte succede qualcosa: conta++;
  • l'accumulatore raccoglie un totale: somma = somma + i (oppure somma += i).
// Somma dei numeri da 1 a N, e quanti di questi sono pari
int somma = 0;      // accumulatore: parte da 0
int pari = 0;       // contatore: parte da 0

for (int i = 1; i <= n; i++)
{
    somma += i;
    if (i % 2 == 0)
    {
        pari++;
    }
}

lblRisultato.Text = "Somma: " + somma + " — numeri pari: " + pari;

Attenzione: se scrivi int somma = 0; dentro le graffe del for, la somma riparte da zero a ogni giro e, finito il ciclo, non esiste più. Le variabili che devono "sopravvivere" al ciclo vanno create prima.

Il ciclo while: ripetere finché serve

Il while ("finché") si usa quando non sai in anticipo quante volte ripetere: continua finché la condizione resta vera.

// Quanti anni servono per raddoppiare un capitale al 5% annuo?
double capitale = 1000;
int anni = 0;

while (capitale < 2000)
{
    capitale = capitale * 1.05;
    anni++;
}

lblRisultato.Text = "Servono " + anni + " anni";   // 15 anni

Il do ... while è uguale, ma controlla la condizione alla fine: il blocco viene eseguito almeno una volta.

int n = 1;
do
{
    txtOutput.AppendText(n + " ");
    n = n * 2;
} while (n <= 100);             // scrive 1 2 4 8 16 32 64 — notare il ; finale

Attenzione al ciclo infinito: se dentro il while non cambia niente che possa rendere falsa la condizione, il ciclo non finisce mai e il form si blocca ("Non risponde"). In Visual Studio si ferma con Maiusc+F5. Controlla sempre che la variabile della condizione venga modificata a ogni giro.

Quale ciclo usare?

SituazioneCiclo
So quante volte ripetere (da 1 a 10, per ogni carattere di una parola…)for
Ripeto finché succede qualcosa (raddoppio, numero trovato…)while
Come while, ma il blocco va eseguito almeno una voltado ... while
Scorro tutti gli elementi di una listaforeach (prossima lezione)

break esce subito dal ciclo; continue salta il resto del giro e passa al successivo.

// Il primo divisore di n maggiore di 1
for (int d = 2; d <= n; d++)
{
    if (n % d == 0)
    {
        lblRisultato.Text = "Il primo divisore è " + d;
        break;          // trovato: inutile continuare
    }
}

Ricordare un valore tra un clic e l'altro

Le variabili create dentro un metodo _Click spariscono alla fine del metodo: al clic successivo ripartono da capo. Se il form deve ricordare qualcosa (quanti clic, un numero segreto, un totale), la variabile va dichiarata fuori dai metodi, dentro la classe del form. Si chiama campo (o variabile del form).

public partial class Form1 : Form
{
    int contaClic = 0;                 // campo: esiste finché il form è aperto

    public Form1()
    {
        InitializeComponent();
    }

    private void btnClic_Click(object sender, EventArgs e)
    {
        contaClic++;                   // non riparte da 0: si ricorda il valore
        lblConta.Text = "Hai cliccato " + contaClic + " volte";
    }

    private void btnAzzera_Click(object sender, EventArgs e)
    {
        contaClic = 0;                 // tutti i metodi del form lo vedono
        lblConta.Text = "Contatore azzerato";
    }
}
Dove la dichiaroChi la vedeQuanto dura
dentro le graffe di un for o di un ifsolo quel bloccofinché il blocco non finisce
dentro un metodo (es. btnClic_Click)solo quel metodofinché il metodo non finisce
fuori dai metodi, nella classe del form (campo)tutti i metodi del formfinché il form è aperto

I metodi: scrivere il codice una volta sola

Un metodo è un blocco di codice con un nome, che puoi chiamare quando ti serve. Ne usi già tanti: MessageBox.Show(...), int.TryParse(...), txtOutput.Clear(). Ora impari a scriverne di tuoi.

Si scrivono dentro la classe del form, fuori dagli altri metodi (per esempio sotto l'ultimo _Click).

private double CelsiusInFahrenheit(double celsius)
{
    double fahrenheit = celsius * 9 / 5 + 32;
    return fahrenheit;
}
PezzoSignificato
privateil metodo si usa solo dentro questo form
doubleil tipo del risultato che il metodo restituisce; void se non restituisce niente
CelsiusInFahrenheitil nome: un verbo o una descrizione chiara, con le iniziali maiuscole
(double celsius)il parametro: il dato che il metodo riceve da chi lo chiama
return fahrenheit;restituisce il risultato e termina il metodo

Ora lo stesso calcolo si usa dove serve, con una riga:

private void btnConverti_Click(object sender, EventArgs e)
{
    double c;
    if (double.TryParse(txtCelsius.Text, out c))
    {
        lblRisultato.Text = CelsiusInFahrenheit(c) + " °F";
    }
}

private void btnTabella_Click(object sender, EventArgs e)
{
    txtOutput.Clear();
    for (int c = -10; c <= 40; c += 10)
    {
        txtOutput.AppendText(c + " °C = " + CelsiusInFahrenheit(c) + " °F" + Environment.NewLine);
    }
}

Se un giorno la formula va corretta, la cambi in un solo punto e tutti e due i bottoni si aggiornano.

Prova: segui la chiamata di un metodo

Premi Passo per eseguire il codice una riga alla volta. Guarda il riquadro delle variabili: quando il programma entra nel metodo, il parametro riceve il valore; quando incontra return, il risultato torna al punto della chiamata.

Variabili
Premi Passo per iniziare.

Tipi di metodi

TipoEsempioSi chiama così
senza parametri, senza risultatoprivate void PulisciForm()PulisciForm();
con parametri, senza risultatoprivate void Avvisa(string testo)Avvisa("Campo vuoto");
con parametri e risultatoprivate int Somma(int a, int b)int s = Somma(3, 4);
che risponde sì o noprivate bool EPari(int n)if (EPari(x)) { ... }
// Un metodo void: rimette il form come all'inizio
private void PulisciForm()
{
    txtNumero.Clear();
    txtOutput.Clear();
    lblRisultato.Text = "";
    txtNumero.Focus();
}

// Un metodo bool: ciclo + return dentro un metodo
private bool EPrimo(int n)
{
    if (n < 2)
    {
        return false;
    }
    for (int d = 2; d * d <= n; d++)
    {
        if (n % d == 0)
        {
            return false;       // trovato un divisore: non è primo, esce subito
        }
    }
    return true;                // nessun divisore trovato
}

Attenzione: un metodo che non è void deve arrivare sempre a un return, qualunque strada prenda. Se manca, Visual Studio segnala l'errore "non tutti i percorsi del codice restituiscono un valore".

Quando creare un metodo? Quando ti accorgi di copiare e incollare lo stesso pezzo di codice, oppure quando un _Click diventa così lungo che non si capisce più cosa fa. Un metodo con un buon nome (CalcolaMedia, EPrimo, PulisciForm) rende il codice leggibile come una frase.

Gli errori più comuni

ErroreCosa succedeCorrezione
for (int i = 1; i <= 10; i++);il ; chiude il for: il blocco sotto viene eseguito una volta solaniente ; dopo la parentesi
i < 10 invece di i <= 10un giro in meno (manca il 10)controlla primo e ultimo valore
accumulatore dentro il cicloriparte da 0 a ogni girodichiaralo prima del ciclo
while senza modificare la variabileciclo infinito, il form non rispondecambia la variabile a ogni giro
contatore dentro il _Clicknon ricorda i clic precedentidichiaralo come campo del form
metodo scritto dentro un altro metodoerrori strani di parentesiogni metodo va fuori dagli altri, dentro la classe
int r = Saluta(); con Saluta voiderrore: un void non restituisce nienteusa return e un tipo di ritorno

Esercizi

Esercizio 1 – Somma e media. Una TextBox txtN e un bottone. Il programma calcola la somma dei numeri da 1 a N e la loro media, e scrive in txtOutput tutti i numeri dispari da 1 a N.

Mostra la soluzione
private void btnCalcola_Click(object sender, EventArgs e)
{
    int n;
    if (!int.TryParse(txtN.Text, out n) || n < 1)
    {
        MessageBox.Show("Scrivi un numero intero maggiore di 0.");
        return;
    }

    txtOutput.Clear();
    int somma = 0;

    for (int i = 1; i <= n; i++)
    {
        somma += i;
        if (i % 2 == 1)
        {
            txtOutput.AppendText(i + " ");
        }
    }

    double media = (double)somma / n;     // (double) evita la divisione tra interi
    lblRisultato.Text = "Somma: " + somma + " — media: " + media;
}

Senza (double), somma / n sarebbe una divisione tra interi e perderebbe i decimali.

Esercizio 2 – Indovina il numero. All'apertura del form il computer sceglie un numero segreto da 1 a 100. L'utente prova con una TextBox e un bottone: il programma risponde "più alto", "più basso" o "Indovinato in N tentativi!". Un secondo bottone ricomincia la partita.

Suggerimento: il numero segreto e i tentativi devono essere campi del form. Un numero casuale si ottiene con Random: new Random().Next(1, 101) dà un numero da 1 a 100.

Mostra la soluzione
public partial class Form1 : Form
{
    Random caso = new Random();
    int segreto;
    int tentativi;

    public Form1()
    {
        InitializeComponent();
        NuovaPartita();
    }

    private void NuovaPartita()
    {
        segreto = caso.Next(1, 101);
        tentativi = 0;
        lblEsito.Text = "Ho pensato un numero da 1 a 100";
        txtTentativo.Clear();
        txtTentativo.Focus();
    }

    private void btnProva_Click(object sender, EventArgs e)
    {
        int numero;
        if (!int.TryParse(txtTentativo.Text, out numero))
        {
            MessageBox.Show("Scrivi un numero.");
            return;
        }

        tentativi++;

        if (numero < segreto)
        {
            lblEsito.Text = numero + ": più alto!";
        }
        else if (numero > segreto)
        {
            lblEsito.Text = numero + ": più basso!";
        }
        else
        {
            lblEsito.Text = "Indovinato in " + tentativi + " tentativi!";
        }

        txtTentativo.SelectAll();
        txtTentativo.Focus();
    }

    private void btnNuova_Click(object sender, EventArgs e)
    {
        NuovaPartita();
    }
}

NuovaPartita() è un metodo usato in due punti: nel costruttore (all'apertura) e nel bottone "Nuova partita".

Esercizio 3 – Numeri primi. Usa il metodo EPrimo della lezione: l'utente scrive N e il programma elenca in txtOutput tutti i numeri primi fino a N, poi scrive quanti sono.

Mostra la soluzione
private void btnPrimi_Click(object sender, EventArgs e)
{
    int n;
    if (!int.TryParse(txtN.Text, out n) || n < 2)
    {
        MessageBox.Show("Scrivi un numero intero da 2 in su.");
        return;
    }

    txtOutput.Clear();
    int quanti = 0;

    for (int i = 2; i <= n; i++)
    {
        if (EPrimo(i))
        {
            txtOutput.AppendText(i + " ");
            quanti++;
        }
    }

    lblRisultato.Text = "Numeri primi trovati: " + quanti;
}

Il ciclo del bottone chiama EPrimo, che a sua volta contiene un altro ciclo: il codice resta corto e leggibile.

Esercizio 4 – Il salvadanaio. Il form ha una TextBox per l'importo e due bottoni, Versa e Preleva. Il saldo (campo del form) parte da 0 e non può andare sotto zero. Scrivi un metodo LeggiImporto() che restituisce l'importo scritto, oppure -1 se non è un numero positivo, e un metodo AggiornaSaldo() che scrive il saldo nella Label. Usali tutti e due in entrambi i bottoni.

Mostra la soluzione
public partial class Form1 : Form
{
    double saldo = 0;

    public Form1()
    {
        InitializeComponent();
        AggiornaSaldo();
    }

    private double LeggiImporto()
    {
        double importo;
        if (double.TryParse(txtImporto.Text, out importo) && importo > 0)
        {
            return importo;
        }
        MessageBox.Show("Scrivi un importo positivo (es. 12,50).");
        return -1;
    }

    private void AggiornaSaldo()
    {
        lblSaldo.Text = "Saldo: " + saldo.ToString("0.00") + " €";
        txtImporto.Clear();
    }

    private void btnVersa_Click(object sender, EventArgs e)
    {
        double importo = LeggiImporto();
        if (importo == -1) return;

        saldo += importo;
        AggiornaSaldo();
    }

    private void btnPreleva_Click(object sender, EventArgs e)
    {
        double importo = LeggiImporto();
        if (importo == -1) return;

        if (importo > saldo)
        {
            MessageBox.Show("Saldo insufficiente.");
            return;
        }

        saldo -= importo;
        AggiornaSaldo();
    }
}

saldo.ToString("0.00") scrive sempre due cifre decimali.

Riepilogo

StrumentoA cosa serve
for (inizio; condizione; passo)ripetere un numero preciso di volte
while (condizione) / do ... whileripetere finché la condizione è vera (do-while almeno una volta)
break / continueuscire dal ciclo / saltare al giro successivo
contatore e accumulatorecontare e sommare durante il ciclo; si dichiarano prima
campo del formvariabile che il form ricorda tra un clic e l'altro
metodo con voidesegue delle istruzioni, non restituisce niente
metodo con tipo e returncalcola e restituisce un risultato
parametrii dati che il metodo riceve da chi lo chiama

📚 Teoria collegata (percorso C# – il linguaggio, esempi in console): Strutture di controllo e Funzioni e metodi.