🚗 Parco Auto: Classi e File I/O

Dalle stringhe agli oggetti: programma con dati strutturati professionali

🎯 Il Grande Salto: Da Stringhe a Oggetti

Nella Lezione 4 hai imparato a usare List<string> per gestire elenchi di dati.
Ma c’è un problema: le stringhe sono limitate!

❌ Il Problema delle Stringhe

Lezione 4 – Approccio con Stringhe
// Prodotto come stringa concatenata
string prodotto = "Laptop - 1200 - Elettronica - 5";
//                  ↑       ↑      ↑             ↑
//                 Nome  Prezzo  Categoria   Quantità

// 😱 PROBLEMI:
// 1. Difficile da modificare (devi separare con Split)
// 2. Errori di battitura (1200 o "1200"?)
// 3. Non puoi ordinare facilmente per prezzo
// 4. Nessun controllo sui dati (prezzo negativo?)

✅ La Soluzione: Classi Personalizzate

Lezione 5 – Approccio con Oggetti
// Auto come OGGETTO con proprietà definite
Automobile auto = new Automobile("Fiat 500", 1200, "Benzina");
auto.CambiaModello("Fiat Panda");    // Facile da modificare!
auto.CambiaPrezzo(15000);

// ✅ VANTAGGI:
// 1. Proprietà chiare e protette
// 2. Validazione automatica dei dati
// 3. Ordinamento automatico per qualsiasi campo
// 4. Controllo completo sui valori
📚 Cosa Imparerai in Questa Lezione
  • Creare Classi in Visual Studio: Procedura passo-passo
  • Campi Privati e Proprietà: Il metodo professionale completo
  • Costruttori: Inizializzare oggetti in modo sicuro
  • List<Automobile>: Liste di oggetti complessi
  • CRUD Completo: Create, Read, Update, Delete
  • Ordinamento Semplice: Ordinare liste facilmente
  • Salvare su File: Guida passo-passo per CSV e TXT

🏗️ Teoria: Le Classi in C#

🤔 Cos’è una Classe?

Una classe è come un “modello” o “stampo” per creare oggetti.
È un contenitore che raggruppa dati (proprietà) e comportamenti (metodi) correlati.

🎯 Analogia del Mondo Reale:

Immagina di voler costruire automobili in fabbrica:

  • CLASSE Automobile: Il progetto/disegno tecnico su carta (esiste 1 sola volta)
  • OGGETTO auto1: Una Fiat 500 rossa costruita dal progetto (reale, fisica)
  • OGGETTO auto2: Una Fiat 500 blu costruita dallo stesso progetto (diversa da auto1)
  • OGGETTO auto3: Un’altra Fiat 500 verde (sempre dal progetto)
┌─────────────────────────────────┐
│   CLASSE Automobile             │  ← Il PROGETTO (su carta)
│   - Modello                     │     Esiste 1 sola volta
│   - Cilindrata                  │
│   - Carburante                  │
└─────────────────────────────────┘
           │
           │ Crea oggetti da questo progetto
           ↓
┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│ auto1        │  │ auto2        │  │ auto3        │
│ Fiat 500     │  │ Fiat 500     │  │ Fiat 500     │
│ 1200cc       │  │ 1200cc       │  │ 1200cc       │
│ Benzina      │  │ Diesel       │  │ GPL          │
│ ROSSA        │  │ BLU          │  │ VERDE        │
└──────────────┘  └──────────────┘  └──────────────┘
    ↑ OGGETTO       ↑ OGGETTO       ↑ OGGETTO
    Copia reale     Copia reale     Copia reale

📝 Come Creare una Classe in Visual Studio

🛠️ Procedura Passo-Passo

STEP 1: Aprire Visual Studio e il Tuo Progetto

  1. Apri Visual Studio
  2. Apri il tuo progetto esistente (es: “ParcoAuto”)

STEP 2: Aggiungere una Nuova Classe

  1. Guarda la finestra Esplora Soluzioni (Solution Explorer) sulla destra
  2. Fai clic destro sul nome del progetto “ParcoAuto”
  3. Nel menu che appare, seleziona: Aggiungi → Nuovo elemento...
  4. Si aprirà una finestra “Aggiungi nuovo elemento”
  5. Nella lista centrale, seleziona Classe (Class)
  6. In basso, nel campo Nome, scrivi: Automobile.cs
  7. Clicca Aggiungi

📸 Visualizzazione Esplora Soluzioni:

Solution Explorer
└── 📁 ParcoAuto  ← CLIC DESTRO QUI!
    ├── 📁 Properties
    ├── 📄 Form1.cs
    ├── 📄 Form1.Designer.cs
    └── 📄 Program.cs

Dopo "Aggiungi → Nuovo elemento → Classe":

Solution Explorer
└── 📁 ParcoAuto
    ├── 📁 Properties
    ├── 📄 Automobile.cs  ← NUOVO FILE!
    ├── 📄 Form1.cs
    ├── 📄 Form1.Designer.cs
    └── 📄 Program.cs
💡 Nota Importante

Visual Studio creerà automaticamente il file Automobile.cs con un template base.
Lo modificheremo nei prossimi step per aggiungere le nostre proprietà!

📋 Anatomia di una Classe – Metodo Professionale

Automobile.cs – La Classe Completa con Campi Privati
using System;

namespace ParcoAuto
{
    // ═══════════════════════════════════════════════
    // CLASSE: Modello per creare oggetti Automobile
    // ═══════════════════════════════════════════════
    public class Automobile
    {
        // ───────────────────────────────────────────
        // CAMPI PRIVATI: Dati nascosti all'esterno
        // ───────────────────────────────────────────
        // Il trattino basso _ indica che è una variabile privata
        private string _modello;
        private int _cilindrata;
        private string _carburante;

        // ───────────────────────────────────────────
        // PROPRIETÀ PUBBLICHE: Accesso controllato ai dati
        // ───────────────────────────────────────────
        
        // Proprietà Modello
        public string Modello
        {
            get 
            { 
                // Quando qualcuno LEGGE il valore
                return _modello; 
            }
            set 
            { 
                // Quando qualcuno SCRIVE il valore
                // Posso fare controlli prima di salvare!
                if (!string.IsNullOrEmpty(value))
                {
                    _modello = value;
                }
            }
        }

        // Proprietà Cilindrata
        public int Cilindrata
        {
            get 
            { 
                return _cilindrata; 
            }
            set 
            { 
                // VALIDAZIONE: cilindrata deve essere ragionevole
                if (value >= 500 && value <= 10000)
                {
                    _cilindrata = value;
                }
                else
                {
                    // Se il valore non è valido, non cambiare niente
                    // (o puoi mostrare un messaggio di errore)
                }
            }
        }

        // Proprietà Carburante
        public string Carburante
        {
            get 
            { 
                return _carburante; 
            }
            set 
            { 
                if (!string.IsNullOrEmpty(value))
                {
                    _carburante = value;
                }
            }
        }

        // ───────────────────────────────────────────
        // COSTRUTTORE: Crea l'auto con valori iniziali
        // ───────────────────────────────────────────
        public Automobile(string modello, int cilindrata, string carburante)
        {
            // Usa le proprietà per avere la validazione automatica
            Modello = modello;
            Cilindrata = cilindrata;
            Carburante = carburante;
        }

        // ───────────────────────────────────────────
        // METODI: Comportamenti dell'auto
        // ───────────────────────────────────────────
        
        // Metodo per visualizzare l'auto formattata
        public string Stampa()
        {
            return $"{Modello} - {Cilindrata}cc - {Carburante}";
        }

        // Override ToString per visualizzazione automatica
        public override string ToString()
        {
            return Stampa();
        }
    }
}

🔍 Perché Serve ToString()? Spiegazione Fondamentale

📖 Il Problema Senza ToString()

Quando aggiungi un oggetto alla ListBox, C# deve sapere COSA VISUALIZZARE.
Se non gli dici nulla, mostrerà una cosa bruttissima e inutile!

❌ SENZA ToString() – Risultato Brutto
public class Automobile
{
    // ... campi e proprietà ...
    
    public string Stampa()
    {
        return $"{Modello} - {Cilindrata}cc - {Carburante}";
    }
    
    // ❌ NON c'è ToString()!
}

// Nel Form:
Automobile auto = new Automobile("Fiat 500", 1200, "Benzina");
lstAuto.Items.Add(auto);

// La ListBox mostra:
┌─────────────────────────────┐
│ ParcoAuto.Automobile        │  ← Brutto! Solo il nome della classe!
└─────────────────────────────┘
✅ CON ToString() – Risultato Perfetto
public class Automobile
{
    // ... campi e proprietà ...
    
    public string Stampa()
    {
        return $"{Modello} - {Cilindrata}cc - {Carburante}";
    }
    
    // ✅ Override ToString()
    public override string ToString()
    {
        return Stampa();  // Chiama il metodo Stampa()
    }
}

// Nel Form:
Automobile auto = new Automobile("Fiat 500", 1200, "Benzina");
lstAuto.Items.Add(auto);

// La ListBox mostra:
┌─────────────────────────────┐
│ Fiat 500 - 1200cc - Benzina │  ← Perfetto! Dati leggibili!
└─────────────────────────────┘

🎬 Cosa Succede Dietro le Quinte

// Quando scrivi:
lstAuto.Items.Add(auto);

// C# fa AUTOMATICAMENTE questo:
lstAuto.Items.Add(auto.ToString());
//                     ↑
//                 Chiama ToString()!

// Se TU hai scritto ToString():
public override string ToString()
{
    return Stampa();  // Ritorna "Fiat 500 - 1200cc - Benzina"
}

// Se NON hai scritto ToString():
// C# usa quello di default che ritorna solo il nome della classe:
// "ParcoAuto.Automobile" ← INUTILE!

💡 Perché “override”?

La parola override significa “sostituisco la versione vecchia con la mia”.

// TUTTI gli oggetti in C# hanno già un ToString()
// che fa una cosa generica (mostra il nome della classe)

// Quando scrivi "override", dici a C#:
// "Ehi, quando qualcuno chiama ToString() sul MIO oggetto,
//  NON usare quello generico, usa il MIO!"

public override string ToString()  // ← "override" = sostituisci!
{
    return Stampa();  // La MIA versione personalizzata
}

🤔 Perché Non Usare Direttamente Stampa()?

Potresti pensare: “Perché non fare così ogni volta?”

// ❌ Opzione 1: Chiamare Stampa() manualmente (SCOMODO)
lstAuto.Items.Add(auto.Stampa());  
// Funziona, ma devi ricordartelo OGNI volta!
// Se dimentichi, vedi "ParcoAuto.Automobile"

// ✅ Opzione 2: Override ToString() (AUTOMATICO)
lstAuto.Items.Add(auto);  
// C# chiama ToString() automaticamente!
// Più comodo, più professionale, meno errori!

Con ToString(), il tuo oggetto “sa presentarsi da solo” in modo automatico!

📊 Confronto Visuale

Situazione Cosa Vedi Risultato
Senza ToString() ParcoAuto.Automobile ❌ Inutile
Con ToString() Fiat 500 - 1200cc - Benzina ✅ Perfetto!

🎯 In Parole Semplici

Senza ToString():

“Ehi ListBox, aggiungi questa auto!”
ListBox: “Ok, ma cosa scrivo? Non lo so… scrivo ‘ParcoAuto.Automobile'” 😕

Con ToString():

“Ehi ListBox, aggiungi questa auto!”
ListBox: “Ok, la chiamo per sapere come presentarsi…”
Auto: “Ciao! Io sono ‘Fiat 500 – 1200cc – Benzina’!” 😊
ListBox: “Perfetto, scrivo questo!”

💡 Regola Pratica da Ricordare

Ogni volta che crei una classe che verrà visualizzata (in ListBox, ComboBox, MessageBox, ecc.),
scrivi SEMPRE ToString() per dire come deve apparire!

// Schema da seguire SEMPRE:
public class MiaClasse
{
    // 1. Campi privati
    private string _dato1;
    private int _dato2;
    
    // 2. Proprietà pubbliche
    public string Dato1 
    { 
        get { return _dato1; } 
        set { _dato1 = value; } 
    }
    
    // 3. Costruttore
    public MiaClasse(string dato1, int dato2)
    {
        Dato1 = dato1;
        Dato2 = dato2;
    }
    
    // 4. Metodo Stampa per formattare
    public string Stampa()
    {
        return $"{_dato1} - {_dato2}";
    }
    
    // 5. ✅ SEMPRE: Override ToString()
    public override string ToString()
    {
        return Stampa();
    }
}

🧪 Dove Viene Usato ToString() Automaticamente?

ToString() viene chiamato automaticamente in MOLTE situazioni:

Automobile auto = new Automobile("Fiat 500", 1200, "Benzina");

// 1. Quando aggiungi a ListBox o ComboBox
lstAuto.Items.Add(auto);  // Chiama auto.ToString()

// 2. Quando concateni con una stringa
string messaggio = "Ho comprato: " + auto;  
// auto viene convertito con ToString()
// Risultato: "Ho comprato: Fiat 500 - 1200cc - Benzina"

// 3. Quando usi in MessageBox
MessageBox.Show("Auto selezionata: " + auto);
// Chiama ToString()

// 4. Quando usi Console.WriteLine
Console.WriteLine(auto);  // Chiama ToString()

// In TUTTI questi casi, se non hai ToString(),
// vedresti solo "ParcoAuto.Automobile" ❌

🔑 Perché Usare Campi Privati + Proprietà?

📖 Spiegazione Semplice

Immagina di avere una cassaforte (campo privato _cilindrata) con una porta speciale (proprietà Cilindrata).
Quando qualcuno vuole mettere soldi nella cassaforte, la porta controlla se i soldi sono veri o falsi prima di accettarli!

🎯 Esempio Pratico:

// Qualcuno prova a impostare una cilindrata assurda:
auto.Cilindrata = -5000;  // Valore assurdo!

// Cosa succede dietro le quinte:
public int Cilindrata
{
    set 
    { 
        if (value >= 500 && value <= 10000)  // Controllo!
        {
            _cilindrata = value;  // OK, salvo
        }
        else
        {
            // RIFIUTO il valore! _cilindrata rimane com'era
        }
    }
}

// Risultato: il valore NON viene cambiato!
// La cassaforte ha rifiutato i "soldi falsi"!

✅ I Vantaggi Spiegati Semplicemente:

  1. Protezione: Nessuno può mettere valori assurdi nei tuoi dati
  2. Controllo: Tu decidi cosa accettare e cosa rifiutare
  3. Sicurezza: Il campo privato (_cilindrata) è nascosto, solo la proprietà è visibile
  4. Flessibilità: Puoi cambiare come funziona internamente senza rompere il codice esistente

🎬 Come Funziona – Passo Passo:

// 1. Crei un'auto
Automobile auto = new Automobile("Fiat 500", 1200, "Benzina");

// 2. Provi a leggere la cilindrata
int valore = auto.Cilindrata;
//            ↓
// Chiama il GET:
// get { return _cilindrata; }  ← Ritorna 1200

// 3. Provi a cambiare la cilindrata
auto.Cilindrata = 2000;
//                ↓
// Chiama il SET:
// set 
// { 
//     if (2000 >= 500 && 2000 <= 10000) ← 2000 è OK! // { // _cilindrata = 2000; ← Salva il nuovo valore // } // } // 4. Provi a mettere un valore assurdo auto.Cilindrata = -5000; // ↓ // Chiama il SET: // set // { // if (-5000 >= 500 && -5000 <= 10000)  ← -5000 NON è OK!
//     {
//         // Non entra qui!
//     }
//     // _cilindrata rimane 2000, non cambia!
// }
🔑 Schema Visuale: Campo Privato + Proprietà
┌─────────────────────────────────────────────────┐
│              CLASSE AUTOMOBILE                  │
│                                                 │
│  ┌─────────────────────────────────┐           │
│  │ CAMPO PRIVATO (Cassaforte)      │           │
│  │ private int _cilindrata;        │           │
│  │ ← NASCOSTO dall'esterno         │           │
│  └─────────────────────────────────┘           │
│                   ↑                             │
│                   │                             │
│  ┌────────────────┴──────────────────┐         │
│  │ PROPRIETÀ PUBBLICA (Porta)        │         │
│  │ public int Cilindrata             │         │
│  │ {                                 │         │
│  │   get { return _cilindrata; }    │← LEGGI  │
│  │   set { /* controlli */ }        │← SCRIVI │
│  │ }                                 │         │
│  └───────────────────────────────────┘         │
└─────────────────────────────────────────────────┘
                    ↑
                    │
        Codice esterno accede SOLO alla proprietà
        auto.Cilindrata = 1200;  ✅
        auto._cilindrata = 1200; ❌ ERRORE! Privato!

🚗 Progetto Base: Parco Auto Semplice

Ora creiamo un’applicazione completa per gestire un parco automobili.
Puoi aggiungere auto, visualizzarle e rimuoverle. Ogni auto è un oggetto con proprietà protette!

1

Interfaccia Grafica

Tipo Controllo Name Scopo
TextBox txtModello Modello auto (es: “Fiat 500”)
TextBox txtCilindrata Cilindrata in cc (es: “1200”)
ComboBox cmbCarburante Tipo carburante (Benzina, Diesel, ecc.)
Button btnAggiungi Aggiungi auto alla lista
Button btnRimuovi Rimuovi auto selezionata
ListBox lstAuto Elenco auto nel parco
Label lblTotaleAuto Mostra quante auto ci sono

2

Codice Completo Form – Spiegato Passo Passo

Form1.cs
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace ParcoAuto
{
    public partial class Form1 : Form
    {
        // ═══════════════════════════════════════
        // VARIABILE GLOBALE: Lista di Automobile
        // ═══════════════════════════════════════
        // Questa lista contiene tutti gli oggetti Automobile
        private List<Automobile> listaAuto;

        public Form1()
        {
            InitializeComponent();
            
            // Crea una nuova lista vuota
            listaAuto = new List<Automobile>();
            
            // Riempi la ComboBox con i tipi di carburante
            InizializzaCarburanti();
        }

        // ═══════════════════════════════════════
        // Riempi la ComboBox con i carburanti
        // ═══════════════════════════════════════
        private void InizializzaCarburanti()
        {
            cmbCarburante.Items.Add("Scegli carburante");
            cmbCarburante.Items.Add("Benzina");
            cmbCarburante.Items.Add("Diesel");
            cmbCarburante.Items.Add("GPL");
            cmbCarburante.Items.Add("Metano");
            cmbCarburante.Items.Add("Elettrica");
            cmbCarburante.Items.Add("Ibrida");
            
            // Seleziona il primo elemento
            cmbCarburante.SelectedIndex = 0;
        }

        // ═══════════════════════════════════════
        // EVENTO: Aggiungi auto (clic su pulsante)
        // ═══════════════════════════════════════
        private void btnAggiungi_Click(object sender, EventArgs e)
        {
            // STEP 1: Controlla che tutti i campi siano compilati
            if (!ControllaCampi())
            {
                return;  // Esci dal metodo se i campi non sono OK
            }

            // STEP 2: Controlla che la cilindrata sia un numero valido
            if (!ValidaCilindrata(txtCilindrata.Text))
            {
                return;  // Esci se la cilindrata non è valida
            }

            // STEP 3: Prendi i valori dalle TextBox
            string modello = txtModello.Text;
            int cilindrata = Convert.ToInt32(txtCilindrata.Text);
            string carburante = cmbCarburante.SelectedItem.ToString();

            // STEP 4: CREA UN NUOVO OGGETTO Automobile
            // Il costruttore riceve i 3 parametri
            Automobile nuovaAuto = new Automobile(modello, cilindrata, carburante);
            
            // STEP 5: Aggiungi l'oggetto alla lista
            listaAuto.Add(nuovaAuto);
            
            // STEP 6: Aggiorna quello che vedi sullo schermo
            AggiornaListaAuto();     // Aggiorna la ListBox
            AggiornaStatistiche();    // Aggiorna il contatore
            PulisciCampi();           // Pulisci le TextBox
            
            // STEP 7: Mostra messaggio di conferma
            MessageBox.Show("Auto aggiunta con successo!", 
                          "Operazione Completata",
                          MessageBoxButtons.OK, 
                          MessageBoxIcon.Information);
        }

        // ═══════════════════════════════════════
        // Controlla che i campi siano compilati
        // ═══════════════════════════════════════
        private bool ControllaCampi()
        {
            // Controlla modello
            if (string.IsNullOrEmpty(txtModello.Text))
            {
                MessageBox.Show("Devi inserire il modello dell'auto!",
                              "Campo Mancante",
                              MessageBoxButtons.OK,
                              MessageBoxIcon.Warning);
                return false;
            }
            
            // Controlla cilindrata
            if (string.IsNullOrEmpty(txtCilindrata.Text))
            {
                MessageBox.Show("Devi inserire la cilindrata!",
                              "Campo Mancante",
                              MessageBoxButtons.OK,
                              MessageBoxIcon.Warning);
                return false;
            }
            
            // Controlla carburante
            if (cmbCarburante.SelectedIndex == 0)
            {
                MessageBox.Show("Devi selezionare il tipo di carburante!",
                              "Campo Mancante",
                              MessageBoxButtons.OK,
                              MessageBoxIcon.Warning);
                return false;
            }
            
            // Se arrivo qui, tutti i campi sono OK
            return true;
        }

        // ═══════════════════════════════════════
        // Controlla che la cilindrata sia valida
        // ═══════════════════════════════════════
        private bool ValidaCilindrata(string testo)
        {
            try
            {
                // Prova a convertire il testo in numero
                int cilindrata = Convert.ToInt32(testo);
                
                // Controlla che sia in un range sensato
                if (cilindrata <= 0 || cilindrata > 10000)
                {
                    MessageBox.Show("La cilindrata deve essere tra 1 e 10000 cc!",
                                  "Valore Non Valido",
                                  MessageBoxButtons.OK,
                                  MessageBoxIcon.Warning);
                    return false;
                }
                
                return true;  // Il valore è OK
            }
            catch
            {
                // Se la conversione fallisce (es: "abc" non è un numero)
                MessageBox.Show("Devi inserire un numero valido per la cilindrata!",
                              "Errore",
                              MessageBoxButtons.OK,
                              MessageBoxIcon.Error);
                return false;
            }
        }
    }
}

🤔 Perché Validare Sia nel Form CHE nella Classe?

📖 Domanda Importante: Non Basta Validare in UN Solo Posto?

Potresti pensare: “Perché fare i controlli due volte? Se controllo nel Form, basta quello!”
Risposta: Sono DUE tipi di validazione con scopi DIVERSI!

🎯 I Due Livelli di Protezione

Tipo Dove Scopo Cosa Fa
Validazione FORM ControllaCampi()
ValidaCilindrata()
Aiutare l’UTENTE Messaggi chiari
“Devi compilare questo campo!”
Validazione CLASSE Proprietà SET Proteggere i DATI Rifiuta valori assurdi
(silenzioso)

1️⃣ Validazione nel FORM – Per l’Esperienza Utente

// Nel Form - Controlli per l'UTENTE
private bool ControllaCampi()
{
    if (string.IsNullOrEmpty(txtModello.Text))
    {
        MessageBox.Show("⚠️ Devi inserire il modello!");
        txtModello.Focus();  // Porta il cursore sul campo
        return false;
    }
    return true;
}

// 👤 ESPERIENZA UTENTE:
// 1. L'utente lascia vuoto il campo modello
// 2. Clicca "Aggiungi"
// 3. Vede subito: "⚠️ Devi inserire il modello!"
// 4. Il cursore va automaticamente sul campo da compilare
// ✅ CHIARO E UTILE! L'utente capisce cosa fare!

2️⃣ Validazione nella CLASSE – Per la Sicurezza dei Dati

// Nella Classe - Protezione SEMPRE ATTIVA
public int Cilindrata
{
    set 
    { 
        if (value >= 500 && value <= 10000)
        {
            _cilindrata = value;  // OK, salva
        }
        else
        {
            // RIFIUTA valori assurdi (silenzioso)
            // NON salva niente
        }
    }
}

// 🛡️ PROTEZIONE:
// Anche se qualcuno scrive PER ERRORE:
auto.Cilindrata = -9999;
// Il valore NON viene salvato!
// I dati rimangono SEMPRE validi! ✅

🎬 Cosa Succederebbe SENZA Una delle Due?

// ════════════════════════════════════════════════════
// SCENARIO A: Solo validazione FORM (senza Classe)
// ════════════════════════════════════════════════════

// Il Form è protetto ✅
// MA in futuro potresti usare la classe altrove:

// Esempio: Carica dati da file
foreach (string riga in File.ReadAllLines("auto.txt"))
{
    string[] parti = riga.Split(';');
    Automobile auto = new Automobile();
    auto.Cilindrata = Convert.ToInt32(parti[1]);
    // ↑ Se il file è corrotto e ha "-999"?
    // ❌ SENZA validazione nella classe: LO ACCETTA!
    // I dati diventano INVALIDI!
}

// ════════════════════════════════════════════════════
// SCENARIO B: Solo validazione CLASSE (senza Form)
// ════════════════════════════════════════════════════

private void btnAggiungi_Click(object sender, EventArgs e)
{
    // ❌ NO controlli nel Form!
    
    // Crea direttamente
    Automobile auto = new Automobile(
        txtModello.Text,           // Potrebbe essere VUOTO!
        Convert.ToInt32(txtCilindrata.Text),  // 💥 CRASH se vuoto!
        ...
    );
}

// 💥 PROBLEMA:
// - Se txtCilindrata è vuoto → CRASH! (Convert fallisce)
// - Se txtCilindrata = "abc" → CRASH!
// - L'utente vede un errore incomprensibile
// ❌ PESSIMA esperienza utente!

// ════════════════════════════════════════════════════
// SCENARIO C: ENTRAMBI (CORRETTO!)
// ════════════════════════════════════════════════════

// Nel Form: messaggi chiari per l'utente ✅
// Nella Classe: protezione sempre attiva ✅

// ✅ RISULTATO:
// 1. L'utente riceve aiuto chiaro
// 2. I dati sono SEMPRE protetti
// 3. La classe è sicura ovunque la usi

💡 Analogia Semplice: La Banca

🏦 Validazione FORM = Sportello con l’impiegato

L’impiegato ti aiuta gentilmente:
“Scusi, manca la sua firma qui.”
“Questo modulo non è compilato correttamente.”
→ Ti GUIDA a fare le cose bene

🔒 Validazione CLASSE = Cassaforte automatica

Anche se l’impiegato sbaglia e prova a mettere soldi falsi,
la cassaforte ha un sensore che li RIFIUTA automaticamente.
Non si fida SOLO dell’impiegato!

✅ Servono ENTRAMBI:

  • L’impiegato (Form) aiuta il cliente
  • La cassaforte (Classe) protegge i soldi SEMPRE

🎯 Quando Usi la Classe SENZA il Form?

Ci sono tanti casi in cui usi gli oggetti SENZA passare dal Form:

// Caso 1: Caricamento da file
List<Automobile> lista = new List<Automobile>();
foreach (string riga in File.ReadAllLines("auto.txt"))
{
    // ... parsing della riga ...
    auto.Cilindrata = valore;  // ← La classe PROTEGGE qui!
}

// Caso 2: Modifica diretta (senza Form)
Automobile auto = listaAuto[indice];
auto.Cilindrata = nuovoValore;  // ← La classe PROTEGGE qui!

// Caso 3: Dati da internet/database
Automobile auto = new Automobile();
auto.Cilindrata = datiDalServer.Cil;  // ← La classe PROTEGGE qui!

// In TUTTI questi casi NON c'è il Form!
// Ma i dati sono COMUNQUE protetti dalla validazione nella classe! ✅
💡 Regola Pratica Finale
Dove Cosa Controllare Come Reagire
FORM • Campi vuoti
• Formato corretto
• Input utente valido
MessageBox chiaro
Focus sul campo
return (esci)
CLASSE • Valori assurdi
• Limiti logici
• Coerenza dati
Rifiuta silenziosamente
NON salvare
(mantieni valore precedente)

🎯 Conclusione: ENTRAMBI = Programmazione PROFESSIONALE!

  • Form: Migliora l’esperienza utente (messaggi chiari e utili)
  • Classe: Protegge i dati da QUALSIASI fonte (Form, file, rete, altro codice)
  • Insieme: App robusta, sicura e user-friendly!
Form1.cs (continua…)
        // ═══════════════════════════════════════
        // Aggiorna la ListBox con tutte le auto
        // ═══════════════════════════════════════
        private void AggiornaListaAuto()
        {
            // STEP 1: Pulisci la ListBox
            lstAuto.Items.Clear();
            
            // STEP 2: Aggiungi ogni auto dalla lista
            foreach (Automobile auto in listaAuto)
            {
                // Quando aggiungi un oggetto Automobile,
                // la ListBox chiama automaticamente il metodo ToString()
                // che ritorna: "Fiat 500 - 1200cc - Benzina"
                lstAuto.Items.Add(auto);
            }
        }

        // ═══════════════════════════════════════
        // Aggiorna il contatore delle auto
        // ═══════════════════════════════════════
        private void AggiornaStatistiche()
        {
            // Conta quante auto ci sono nella lista
            int numeroAuto = listaAuto.Count;
            
            // Mostra il numero nella Label
            lblTotaleAuto.Text = $"Totale auto nel parco: {numeroAuto}";
        }

        // ═══════════════════════════════════════
        // Pulisci tutti i campi del form
        // ═══════════════════════════════════════
        private void PulisciCampi()
        {
            txtModello.Text = "";
            txtCilindrata.Text = "";
            cmbCarburante.SelectedIndex = 0;
            
            // Metti il cursore sul primo campo
            txtModello.Focus();
        }

        // ═══════════════════════════════════════
        // EVENTO: Rimuovi auto (clic su pulsante)
        // ═══════════════════════════════════════
        private void btnRimuovi_Click(object sender, EventArgs e)
        {
            // Controlla se hai selezionato un'auto
            if (lstAuto.SelectedIndex == -1)
            {
                MessageBox.Show("Devi selezionare un'auto da rimuovere!",
                              "Nessuna Selezione",
                              MessageBoxButtons.OK,
                              MessageBoxIcon.Warning);
                return;
            }

            // Prendi l'auto selezionata dalla lista
            Automobile autoSelezionata = listaAuto[lstAuto.SelectedIndex];
            
            // Salva il modello per il messaggio
            string modello = autoSelezionata.Modello;
            
            // Rimuovi l'auto dalla lista
            listaAuto.RemoveAt(lstAuto.SelectedIndex);
            
            // Aggiorna la visualizzazione
            AggiornaListaAuto();
            AggiornaStatistiche();
            
            // Mostra messaggio di conferma
            MessageBox.Show($"L'auto '{modello}' è stata rimossa!",
                          "Auto Rimossa",
                          MessageBoxButtons.OK,
                          MessageBoxIcon.Information);
        }
    }
}

✏️ Funzionalità MODIFICA – Spiegazione Completa

🎯 Come Funziona la Modifica?

Per modificare un’auto esistente, usiamo una variabile di stato che ci dice se siamo in “modalità aggiungi” o “modalità modifica”.
Il bello è che NON serve un pulsante separato: lo stesso pulsante “Aggiungi” cambia funzione automaticamente!

💡 UN SOLO PULSANTE per Due Funzioni!

Non ti serve aggiungere un pulsante “Modifica” separato! Il pulsante “Aggiungi” è intelligente:

  • 📝 Normalmente: Si chiama “Aggiungi” e aggiunge nuove auto
  • ✏️ Quando selezioni un’auto: Cambia in “Salva Modifiche” e modifica l’auto selezionata
┌────────────────────────────────────┐
│ Modalità NORMALE (Aggiungi)       │
├────────────────────────────────────┤
│ [Modello]  [_____________]         │
│ [Cilindrata] [___________]         │
│                                    │
│ [ Aggiungi ] ← Pulsante NORMALE   │
│                                    │
│ ListBox:                           │
│   BMW Serie 3                      │
│   Tesla Model 3                    │
└────────────────────────────────────┘

        ↓ CLICCHI su "BMW Serie 3"

┌────────────────────────────────────┐
│ Modalità MODIFICA                  │
├────────────────────────────────────┤
│ [Modello]  [_BMW Serie 3___]       │
│ [Cilindrata] [__2000_______]       │
│                                    │
│ [ ✅ Salva Modifiche ] ← CAMBIATO!│
│    (Arancione)                     │
│                                    │
│ ListBox:                           │
│   BMW Serie 3 ← Selezionata       │
│   Tesla Model 3                    │
└────────────────────────────────────┘

1

NON Serve Aggiungere Altri Pulsanti!

Usa solo i pulsanti che hai già:

  • btnAggiungi → Fa sia “Aggiungi” che “Modifica”
  • btnRimuovi → Rimuove l’auto selezionata
  • btnAnnulla (OPZIONALE) → Annulla la modifica e torna in modalità Aggiungi

2

Codice Completo per la Modifica – Spiegato Passo Passo

Form1.cs – Funzionalità Modifica con UN SOLO Pulsante
public partial class Form1 : Form
{
    private List<Automobile> listaAuto;
    
    // ═══════════════════════════════════════
    // VARIABILE DI STATO: Controlla la modalità
    // ═══════════════════════════════════════
    // -1 = modalità AGGIUNGI (default)
    // Altro numero = modalità MODIFICA (indice dell'auto da modificare)
    private int indiceModifica = -1;

    // ... altri metodi ...

    // ═══════════════════════════════════════
    // EVENTO: Quando CLICCHI su un'auto nella ListBox
    // ═══════════════════════════════════════
    // Questo evento si attiva AUTOMATICAMENTE quando selezioni un'auto!
    private void lstAuto_SelectedIndexChanged(object sender, EventArgs e)
    {
        // STEP 1: Controlla se hai davvero selezionato qualcosa
        if (lstAuto.SelectedIndex == -1)
        {
            return;  // Niente selezionato, esci
        }

        // STEP 2: Prendi l'auto selezionata dalla lista
        // lstAuto.SelectedIndex ti dice QUALE riga hai cliccato (0, 1, 2, ecc.)
        Automobile autoSelezionata = listaAuto[lstAuto.SelectedIndex];
        
        // STEP 3: Riempi i campi del form con i dati dell'auto
        // In questo modo l'utente può vedere e modificare i valori
        txtModello.Text = autoSelezionata.Modello;
        txtCilindrata.Text = autoSelezionata.Cilindrata.ToString();
        cmbCarburante.SelectedItem = autoSelezionata.Carburante;
        
        // STEP 4: Salva l'indice dell'auto che stai modificando
        indiceModifica = lstAuto.SelectedIndex;
        
        // STEP 5: CAMBIA IL PULSANTE per indicare la modalità MODIFICA
        btnAggiungi.Text = "✅ Salva Modifiche";
        btnAggiungi.BackColor = Color.Orange;  // Colore diverso per attirare l'attenzione
        
        MessageBox.Show("Ora puoi modificare i dati e cliccare 'Salva Modifiche'",
                      "Modalità Modifica Attivata",
                      MessageBoxButtons.OK,
                      MessageBoxIcon.Information);
    }

    // ═══════════════════════════════════════
    // EVENTO: Aggiungi O Modifica (in base alla modalità)
    // ═══════════════════════════════════════
    // Questo è lo STESSO pulsante di prima!
    // MA ora è "intelligente" e sa cosa fare!
    private void btnAggiungi_Click(object sender, EventArgs e)
    {
        // STEP 1: Validazione (sempre necessaria!)
        if (!ControllaCampi() || !ValidaCilindrata(txtCilindrata.Text))
        {
            return;
        }

        // STEP 2: Raccogli i dati dai campi
        string modello = txtModello.Text;
        int cilindrata = Convert.ToInt32(txtCilindrata.Text);
        string carburante = cmbCarburante.SelectedItem.ToString();

        // STEP 3: Crea un nuovo oggetto Automobile
        Automobile auto = new Automobile(modello, cilindrata, carburante);

        // STEP 4: Controlla la modalità e decidi cosa fare
        if (indiceModifica == -1)
        {
            // ═══════════════════════════════════
            // MODALITÀ AGGIUNGI (normale)
            // ═══════════════════════════════════
            listaAuto.Add(auto);
            
            MessageBox.Show("Auto aggiunta con successo!",
                          "Operazione Completata",
                          MessageBoxButtons.OK,
                          MessageBoxIcon.Information);
        }
        else
        {
            // ═══════════════════════════════════
            // MODALITÀ MODIFICA
            // ═══════════════════════════════════
            // Sostituisci l'auto vecchia con quella nuova
            listaAuto[indiceModifica] = auto;
            
            // IMPORTANTE: Torna in modalità AGGIUNGI
            indiceModifica = -1;
            btnAggiungi.Text = "Aggiungi";
            btnAggiungi.BackColor = SystemColors.Control;  // Colore normale
            
            MessageBox.Show("Auto modificata con successo!",
                          "Modifica Completata",
                          MessageBoxButtons.OK,
                          MessageBoxIcon.Information);
        }

        // STEP 5: Aggiorna visualizzazione e pulisci campi
        AggiornaListaAuto();
        AggiornaStatistiche();
        PulisciCampi();
    }

    // ═══════════════════════════════════════
    // PULSANTE ANNULLA (Opzionale ma consigliato!)
    // ═══════════════════════════════════════
    // Aggiungi questo pulsante solo SE vuoi dare all'utente
    // la possibilità di annullare una modifica
    private void btnAnnulla_Click(object sender, EventArgs e)
    {
        // Torna in modalità AGGIUNGI senza salvare modifiche
        indiceModifica = -1;
        btnAggiungi.Text = "Aggiungi";
        btnAggiungi.BackColor = SystemColors.Control;
        
        PulisciCampi();
        
        MessageBox.Show("Modifica annullata",
                      "Annullato",
                      MessageBoxButtons.OK,
                      MessageBoxIcon.Information);
    }

    // ... altri metodi ...
}

3

Come Funziona – Spiegazione Visuale

┌─────────────────────────────────────────────────────┐
│ SCENARIO 1: AGGIUNGI (indiceModifica = -1)         │
└─────────────────────────────────────────────────────┘

1. Utente compila i campi
   txtModello: "Fiat 500"
   txtCilindrata: "1200"

2. Clicca "Aggiungi"

3. Controllo:
   if (indiceModifica == -1)  ← TRUE!
   {
       listaAuto.Add(auto);  ← AGGIUNGI alla fine
   }

4. Lista DOPO:
   [0] BMW Serie 3
   [1] Tesla Model 3
   [2] Fiat 500  ← NUOVA AUTO aggiunta!


┌─────────────────────────────────────────────────────┐
│ SCENARIO 2: MODIFICA (indiceModifica = 0)          │
└─────────────────────────────────────────────────────┘

1. Utente CLICCA su "BMW Serie 3" nella ListBox
   → lstAuto_SelectedIndexChanged viene chiamato

2. Il metodo fa:
   autoSelezionata = listaAuto[0];  ← Prendi BMW
   txtModello.Text = "BMW Serie 3";  ← Riempi i campi
   txtCilindrata.Text = "2000";
   
   indiceModifica = 0;  ← Salva l'indice!
   btnAggiungi.Text = "✅ Salva Modifiche";

3. Utente MODIFICA i campi:
   txtModello: "BMW Serie 5"  ← CAMBIATO!
   txtCilindrata: "3000"      ← CAMBIATO!

4. Utente clicca "Salva Modifiche"

5. Controllo:
   if (indiceModifica == -1)  ← FALSE!
   else  ← Entra qui!
   {
       listaAuto[0] = auto;  ← SOSTITUISCI all'indice 0
   }

6. Lista DOPO:
   [0] BMW Serie 5 - 3000cc  ← AUTO MODIFICATA!
   [1] Tesla Model 3
   [2] Fiat 500

7. Torna in modalità AGGIUNGI:
   indiceModifica = -1;
   btnAggiungi.Text = "Aggiungi";

🔑 Concetti Chiave

1️⃣ Variabile di Stato: indiceModifica

private int indiceModifica = -1;

// -1 = Modalità AGGIUNGI (nessuna modifica in corso)
// 0, 1, 2, ecc. = Modalità MODIFICA (modifica l'auto a quell'indice)

// Esempio:
if (indiceModifica == -1)
{
    // AGGIUNGI: crea una nuova auto
    listaAuto.Add(auto);
}
else
{
    // MODIFICA: sostituisci l'auto esistente
    listaAuto[indiceModifica] = auto;
}

2️⃣ Evento SelectedIndexChanged

// Si attiva quando l'utente CLICCA su un elemento della ListBox
private void lstAuto_SelectedIndexChanged(object sender, EventArgs e)
{
    // lstAuto.SelectedIndex = quale riga hai cliccato (0, 1, 2, ...)
    // -1 = niente selezionato
    
    if (lstAuto.SelectedIndex != -1)
    {
        Automobile auto = listaAuto[lstAuto.SelectedIndex];
        // Ora hai l'auto selezionata!
    }
}

3️⃣ Riempire i Campi dal l’Oggetto

// Prendi i dati dall'oggetto e mettili nelle TextBox
txtModello.Text = auto.Modello;           // String → TextBox
txtCilindrata.Text = auto.Cilindrata.ToString();  // int → string → TextBox
cmbCarburante.SelectedItem = auto.Carburante;     // String → ComboBox

4️⃣ Sostituire l’Elemento nella Lista

// SBAGLIATO - Modifica solo in memoria locale:
Automobile temp = listaAuto[0];
temp.Modello = "Nuovo";  // Questo FUNZIONA, ma è confuso

// CORRETTO - Crea un nuovo oggetto e sostituisci:
Automobile nuovaAuto = new Automobile("Nuovo", 1500, "GPL");
listaAuto[0] = nuovaAuto;  // Sostituisci completamente l'auto

💡 Interfaccia Utente Migliorata

Per rendere chiaro all’utente in che modalità si trova:

// Modalità AGGIUNGI
btnAggiungi.Text = "Aggiungi";
btnAggiungi.BackColor = SystemColors.Control;  // Grigio normale
lblStato.Text = "Modalità: Aggiungi nuova auto";

// Modalità MODIFICA
btnAggiungi.Text = "✅ Salva Modifiche";
btnAggiungi.BackColor = Color.Orange;  // Arancione per attirare attenzione
lblStato.Text = $"Modalità: Modifica auto '{auto.Modello}'";
⚠️ Errori Comuni da Evitare

❌ Errore 1: Dimenticare di resettare indiceModifica

// SBAGLIATO:
if (indiceModifica != -1)
{
    listaAuto[indiceModifica] = auto;
    // ❌ Dimenticato: indiceModifica = -1;
}

// Problema: rimane in modalità modifica!
// Il prossimo clic su "Aggiungi" modificherà di nuovo!

❌ Errore 2: Non controllare SelectedIndex prima di usarlo

// SBAGLIATO:
private void lstAuto_SelectedIndexChanged(object sender, EventArgs e)
{
    Automobile auto = listaAuto[lstAuto.SelectedIndex];
    // ❌ Se SelectedIndex = -1, CRASH!
}

// CORRETTO:
private void lstAuto_SelectedIndexChanged(object sender, EventArgs e)
{
    if (lstAuto.SelectedIndex == -1)
        return;  // ✅ Controlla sempre!
    
    Automobile auto = listaAuto[lstAuto.SelectedIndex];
}

✅ Soluzione: Usa sempre questo pattern

// PATTERN SICURO per la modifica:

// 1. All'inizio del form:
private int indiceModifica = -1;

// 2. Quando selezioni:
private void lstAuto_SelectedIndexChanged(...)
{
    if (lstAuto.SelectedIndex == -1) return;
    // ... codice ...
    indiceModifica = lstAuto.SelectedIndex;
    btnAggiungi.Text = "Salva Modifiche";
}

// 3. Quando salvi:
private void btnAggiungi_Click(...)
{
    // ... validazione ...
    
    if (indiceModifica == -1)
    {
        listaAuto.Add(auto);
    }
    else
    {
        listaAuto[indiceModifica] = auto;
        indiceModifica = -1;  // ✅ RESET!
        btnAggiungi.Text = "Aggiungi";
    }
    
    AggiornaListaAuto();
}

🎬 Cosa Succede Quando Aggiungi un’Auto – Passo Passo

┌──────────────────────────────────────────────────┐
│ STEP 1: Utente riempie i campi                  │
│ txtModello: "Fiat 500"                           │
│ txtCilindrata: "1200"                            │
│ cmbCarburante: "Benzina"                         │
└──────────────────────────────────────────────────┘
                    ↓
┌──────────────────────────────────────────────────┐
│ STEP 2: Utente clicca "Aggiungi"                │
│ → Il metodo btnAggiungi_Click viene eseguito    │
└──────────────────────────────────────────────────┘
                    ↓
┌──────────────────────────────────────────────────┐
│ STEP 3: Controlli di validazione                │
│ ✅ Modello compilato                            │
│ ✅ Cilindrata compilata                         │
│ ✅ Cilindrata è un numero (1200)                │
│ ✅ Cilindrata tra 1 e 10000                     │
│ ✅ Carburante selezionato                       │
└──────────────────────────────────────────────────┘
                    ↓
┌──────────────────────────────────────────────────┐
│ STEP 4: Crea l'oggetto Automobile               │
│                                                  │
│ new Automobile("Fiat 500", 1200, "Benzina")     │
│                                                  │
│ Il COSTRUTTORE fa:                              │
│ Modello = "Fiat 500"                            │
│ ↓ chiama il SET della proprietà Modello        │
│ ↓ controlla che non sia vuoto                  │
│ ↓ salva in _modello                            │
│                                                  │
│ Cilindrata = 1200                               │
│ ↓ chiama il SET della proprietà Cilindrata     │
│ ↓ controlla che sia tra 500 e 10000            │
│ ↓ salva in _cilindrata                         │
│                                                  │
│ Carburante = "Benzina"                          │
│ ↓ chiama il SET della proprietà Carburante     │
│ ↓ controlla che non sia vuoto                  │
│ ↓ salva in _carburante                         │
└──────────────────────────────────────────────────┘
                    ↓
┌──────────────────────────────────────────────────┐
│ STEP 5: Oggetto creato e pronto!                │
│                                                  │
│ nuovaAuto = {                                   │
│     _modello: "Fiat 500",                       │
│     _cilindrata: 1200,                          │
│     _carburante: "Benzina"                      │
│ }                                               │
└──────────────────────────────────────────────────┘
                    ↓
┌──────────────────────────────────────────────────┐
│ STEP 6: Aggiungi alla lista                     │
│ listaAuto.Add(nuovaAuto)                         │
│                                                  │
│ La lista ora contiene:                          │
│ [0] → Automobile {Fiat 500, 1200, Benzina}      │
└──────────────────────────────────────────────────┘
                    ↓
┌──────────────────────────────────────────────────┐
│ STEP 7: Aggiorna la ListBox                     │
│ lstAuto.Items.Add(nuovaAuto)                     │
│                                                  │
│ Internamente chiama: nuovaAuto.ToString()       │
│ Che ritorna: "Fiat 500 - 1200cc - Benzina"     │
│                                                  │
│ La ListBox mostra:                              │
│ ┌──────────────────────────────┐               │
│ │ Fiat 500 - 1200cc - Benzina  │               │
│ └──────────────────────────────┘               │
└──────────────────────────────────────────────────┘

💾 Salvare i Dati su File – Guida Dettagliata

Quando chiudi il programma, tutti i dati spariscono!
Per evitare questo, impariamo a salvare il parco auto su file. Quando riapri il programma, potrai ricaricare i dati.

📖 Cos’è un File e Come Funziona

Un file è come un quaderno dove scrivi informazioni che vuoi conservare. Quando spegni il computer, il quaderno rimane lì con tutto quello che hai scritto!

🎯 Analogia Semplice:

  • RAM (memoria temporanea): Una lavagna che si cancella quando spegni il PC
  • FILE (memoria permanente): Un quaderno che rimane anche dopo
┌─────────────────────────────────────┐
│ QUANDO IL PROGRAMMA È APERTO:      │
│                                     │
│ RAM (Lavagna)                       │
│ ├── List<Automobile> listaAuto      │
│ │   ├── Fiat 500                    │
│ │   ├── BMW Serie 3                 │
│ │   └── Tesla Model 3               │
│                                     │
│ FILE (Quaderno)                     │
│ └── parco_auto.txt (vuoto o vecchio)│
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ QUANDO CLICCHI "Salva":             │
│                                     │
│ RAM → COPIA → FILE                  │
│                                     │
│ FILE (Quaderno)                     │
│ └── parco_auto.txt                  │
│     Fiat 500 - 1200cc - Benzina     │
│     BMW Serie 3 - 2000cc - Diesel   │
│     Tesla Model 3 - 0cc - Elettrica │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ QUANDO CHIUDI IL PROGRAMMA:         │
│                                     │
│ RAM (Lavagna)                       │
│ └── [TUTTO CANCELLATO] ❌           │
│                                     │
│ FILE (Quaderno)                     │
│ └── parco_auto.txt ✅ RIMANE!       │
│     Fiat 500 - 1200cc - Benzina     │
│     BMW Serie 3 - 2000cc - Diesel   │
│     Tesla Model 3 - 0cc - Elettrica │
└─────────────────────────────────────┘

💾 Metodo 1: Salvare in Formato TXT (Leggibile)

1

Aggiungi il Pulsante di Salvataggio

  • Trascina un Button nel form
  • Chiamalo btnSalvaTXT
  • Imposta la proprietà Text su “Salva in TXT”

2

Codice per Salvare – Spiegato Riga per Riga

Salvataggio in TXT – Versione Commentata
// In cima al file, aggiungi questo:
using System.IO;  // Necessario per lavorare con i file!

// ═══════════════════════════════════════
// EVENTO: Salva in TXT
// ═══════════════════════════════════════
private void btnSalvaTXT_Click(object sender, EventArgs e)
{
    // STEP 1: Usa try-catch per gestire eventuali errori
    // (es: disco pieno, permessi mancanti, ecc.)
    try
    {
        // STEP 2: Crea/Apri il file per scrivere
        // "parco_auto.txt" è il nome del file
        // Se non esiste, viene creato
        // Se esiste, viene sovrascritto
        using (StreamWriter scrittore = new StreamWriter("parco_auto.txt"))
        {
            // STEP 3: Scrivi l'intestazione
            scrittore.WriteLine("═══════════════════════════════════════");
            scrittore.WriteLine("     PARCO AUTO - ELENCO COMPLETO");
            scrittore.WriteLine("═══════════════════════════════════════");
            scrittore.WriteLine("");  // Riga vuota
            
            // STEP 4: Scrivi ogni auto
            int numero = 1;  // Contatore per numerare le auto
            
            foreach (Automobile auto in listaAuto)
            {
                // Scrivi il numero e i dati dell'auto
                scrittore.WriteLine($"{numero}. {auto.Stampa()}");
                scrittore.WriteLine("");  // Riga vuota dopo ogni auto
                
                numero++;  // Passa alla prossima auto
            }
            
            // STEP 5: Scrivi il totale
            scrittore.WriteLine("───────────────────────────────────────");
            scrittore.WriteLine($"Totale auto: {listaAuto.Count}");
            scrittore.WriteLine("═══════════════════════════════════════");
            
        } // ← Il file viene chiuso automaticamente qui!
        
        // STEP 6: Mostra messaggio di successo
        MessageBox.Show("Parco auto salvato nel file parco_auto.txt!",
                      "Salvataggio Completato",
                      MessageBoxButtons.OK,
                      MessageBoxIcon.Information);
    }
    catch (Exception errore)
    {
        // Se qualcosa va storto, mostra il messaggio di errore
        MessageBox.Show($"Errore durante il salvataggio: {errore.Message}",
                      "Errore",
                      MessageBoxButtons.OK,
                      MessageBoxIcon.Error);
    }
}
📄 Come Appare il File Salvato
parco_auto.txt (puoi aprirlo con Notepad)
═══════════════════════════════════════
     PARCO AUTO - ELENCO COMPLETO
═══════════════════════════════════════

1. Fiat 500 - 1200cc - Benzina

2. BMW Serie 3 - 2000cc - Diesel

3. Tesla Model 3 - 0cc - Elettrica

───────────────────────────────────────
Totale auto: 3
═══════════════════════════════════════

🔍 Dove Trovare il File?

  1. Apri Esplora Risorse
  2. Vai alla cartella del tuo progetto (es: C:\Users\TuoNome\source\repos\ParcoAuto)
  3. Apri la cartella bin
  4. Apri la cartella Debug
  5. Troverai il file parco_auto.txt
C:\
└── Users\
    └── TuoNome\
        └── source\
            └── repos\
                └── ParcoAuto\         ← Cartella progetto
                    └── bin\
                        └── Debug\
                            └── parco_auto.txt  ← QUI!

📊 Metodo 2: Salvare in Formato CSV (Excel)

Il formato CSV (Comma-Separated Values) è perfetto se vuoi aprire i dati con Excel o fare calcoli.

1

Aggiungi il Pulsante CSV

  • Trascina un altro Button nel form
  • Chiamalo btnSalvaCSV
  • Imposta la proprietà Text su “Salva in CSV”

2

Codice per CSV – Versione Semplificata

Salvataggio in CSV
// ═══════════════════════════════════════
// EVENTO: Salva in CSV
// ═══════════════════════════════════════
private void btnSalvaCSV_Click(object sender, EventArgs e)
{
    try
    {
        // Apri/Crea il file CSV
        using (StreamWriter scrittore = new StreamWriter("parco_auto.csv"))
        {
            // STEP 1: Scrivi l'intestazione (nomi delle colonne)
            // Usiamo il punto e virgola (;) come separatore
            scrittore.WriteLine("Modello;Cilindrata;Carburante");
            
            // STEP 2: Scrivi ogni auto come una riga
            foreach (Automobile auto in listaAuto)
            {
                // Costruisci la riga con i dati separati da ;
                string riga = $"{auto.Modello};{auto.Cilindrata};{auto.Carburante}";
                scrittore.WriteLine(riga);
            }
        }
        
        // Mostra messaggio di successo
        MessageBox.Show("File CSV salvato! Puoi aprirlo con Excel.",
                      "Salvataggio Completato",
                      MessageBoxButtons.OK,
                      MessageBoxIcon.Information);
    }
    catch (Exception errore)
    {
        MessageBox.Show($"Errore: {errore.Message}",
                      "Errore",
                      MessageBoxButtons.OK,
                      MessageBoxIcon.Error);
    }
}
📊 Come Appare il File CSV
parco_auto.csv (si apre con Excel)
Modello;Cilindrata;Carburante
Fiat 500;1200;Benzina
BMW Serie 3;2000;Diesel
Tesla Model 3;0;Elettrica

Quando apri questo file con Excel, vedrai i dati organizzati in colonne,
pronti per essere modificati o usati per creare grafici!

🔑 Concetti Chiave – StreamWriter Spiegato Semplice

📝 Cos’è StreamWriter?

StreamWriter è uno “scrittore di file”. È come una penna che scrive sul tuo quaderno (file).

// Crea lo "scrittore" e apri il file
using (StreamWriter scrittore = new StreamWriter("miofile.txt"))
{
    // Scrivi una riga e vai a capo
    scrittore.WriteLine("Prima riga");
    
    // Scrivi senza andare a capo
    scrittore.Write("Seconda ");
    scrittore.Write("riga");
    
    // Vai a capo manualmente
    scrittore.WriteLine("");  // Riga vuota
    
} // ← Il file viene chiuso automaticamente qui!

🎯 I Due Metodi Principali:

Metodo Cosa Fa Esempio
WriteLine() Scrive e va a capo scrittore.WriteLine("Ciao");
Write() Scrive senza andare a capo scrittore.Write("Ciao");

💡 Perché “using”?

Il blocco using è importantissimo! Garantisce che il file venga chiuso correttamente
anche se succede un errore. Senza using, il file potrebbe rimanere bloccato!

// ❌ MODO SBAGLIATO (senza using):
StreamWriter scrittore = new StreamWriter("file.txt");
scrittore.WriteLine("Test");
scrittore.Close();  // Se c'è un errore prima, il file non si chiude!

// ✅ MODO CORRETTO (con using):
using (StreamWriter scrittore = new StreamWriter("file.txt"))
{
    scrittore.WriteLine("Test");
} // ← Si chiude SEMPRE automaticamente, anche con errori!

🎬 Cosa Succede Dietro le Quinte:

// Quando scrivi questo:
using (StreamWriter sw = new StreamWriter("file.txt"))
{
    sw.WriteLine("Ciao");
    sw.WriteLine("Mondo");
}

// Succede questo passo-passo:

// 1. Crea un nuovo file "file.txt" (o lo svuota se esiste)
// 2. Apri il file in modalità scrittura
// 3. Scrivi "Ciao" e vai a capo
// 4. Scrivi "Mondo" e vai a capo
// 5. Chiudi il file salvando tutto
// 6. Rilascia le risorse

// Il file ora contiene:
// Ciao
// Mondo

📖 Leggere i Dati dai File – StreamReader

🤔 Il Problema: I Dati Scompaiono!

Hai imparato a salvare i dati su file con StreamWriter.
Ma cosa succede quando chiudi il programma e lo riapri? La lista è vuota! 😢

Per risolvere questo problema, devi imparare a LEGGERE i file e ricaricare i dati!

🔄 Il Ciclo Completo dei Dati

┌─────────────────────────────────────────────────┐
│ 1. INIZIO PROGRAMMA                             │
│    └─→ Leggi file CSV (StreamReader) 📖         │
│        └─→ Riempi List<Automobile>               │
│            └─→ Mostra nella ListBox             │
└─────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────┐
│ 2. UTENTE LAVORA                                │
│    └─→ Aggiunge auto                            │
│    └─→ Modifica auto                            │
│    └─→ Rimuove auto                             │
└─────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────┐
│ 3. SALVATAGGIO                                  │
│    └─→ Scrivi file CSV (StreamWriter) 💾       │
│        └─→ Salva List<Automobile> su disco      │
└─────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────┐
│ 4. CHIUDE PROGRAMMA                             │
│    └─→ I dati sono al SICURO nel file!         │
└─────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────┐
│ 5. RIAPRE PROGRAMMA                             │
│    └─→ Torna al punto 1! 🔄                     │
└─────────────────────────────────────────────────┘

📖 StreamReader – I Metodi Base

1

Controllare se un File Esiste

PRIMA di leggere un file, devi sempre controllare se esiste!

Controllo File.Exists()
using System.IO;  // ← Necessario per File.Exists()

// Controlla se il file esiste
if (File.Exists("automobile.csv"))
{
    // ✅ Il file esiste, puoi leggerlo
    Console.WriteLine("File trovato!");
}
else
{
    // ❌ Il file NON esiste
    MessageBox.Show("File non trovato! Devi salvare prima.");
}

2

Leggere Tutto il File in Una Volta

Metodo 1: ReadToEnd()
// ═══════════════════════════════════════════════
// Legge TUTTO il contenuto del file
// ═══════════════════════════════════════════════
private void btnLeggiTutto_Click(object sender, EventArgs e)
{
    // STEP 1: Controlla se esiste
    if (!File.Exists("parco_auto.txt"))
    {
        MessageBox.Show("File non trovato!");
        return;
    }

    try
    {
        // STEP 2: Apri il file in LETTURA
        using (StreamReader lettore = new StreamReader("parco_auto.txt"))
        {
            // STEP 3: Leggi TUTTO il contenuto
            string tuttoIlTesto = lettore.ReadToEnd();
            
            // STEP 4: Mostra il contenuto
            MessageBox.Show(tuttoIlTesto, "Contenuto File");
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show($"Errore: {ex.Message}");
    }
}

🔑 Metodi di StreamReader

Metodo Cosa Fa
ReadToEnd() Legge TUTTO il file in una stringa
ReadLine() Legge UNA riga alla volta
EndOfStream true se sei arrivato alla fine

3

Leggere Riga per Riga – Il Metodo Migliore

Metodo 2: ReadLine() in un Ciclo
// ═══════════════════════════════════════════════
// Legge il file riga per riga
// ═══════════════════════════════════════════════
private void btnLeggiRighe_Click(object sender, EventArgs e)
{
    if (!File.Exists("parco_auto.txt"))
    {
        MessageBox.Show("File non trovato!");
        return;
    }

    try
    {
        using (StreamReader lettore = new StreamReader("parco_auto.txt"))
        {
            // Leggi riga per riga finché non arrivi alla fine
            while (!lettore.EndOfStream)
            {
                // Leggi UNA riga
                string riga = lettore.ReadLine();
                
                // Elabora la riga (es: mostrala)
                Console.WriteLine(riga);
            }
        }
        
        MessageBox.Show("File letto con successo!");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"Errore: {ex.Message}");
    }
}

🎬 Come Funziona il Ciclo while

// File contiene 3 righe:
// Riga 1: "Fiat 500 - 1200cc - Benzina"
// Riga 2: "BMW Serie 3 - 2000cc - Diesel"
// Riga 3: "Tesla Model 3 - 0cc - Elettrica"

using (StreamReader lettore = new StreamReader("auto.txt"))
{
    // PRIMA ITERAZIONE:
    lettore.EndOfStream → false (ci sono righe)
    riga = lettore.ReadLine() → "Fiat 500 - 1200cc - Benzina"
    
    // SECONDA ITERAZIONE:
    lettore.EndOfStream → false
    riga = lettore.ReadLine() → "BMW Serie 3 - 2000cc - Diesel"
    
    // TERZA ITERAZIONE:
    lettore.EndOfStream → false
    riga = lettore.ReadLine() → "Tesla Model 3 - 0cc - Elettrica"
    
    // QUARTA ITERAZIONE:
    lettore.EndOfStream → true (FINE!)
    while ESCE dal ciclo
}

💾 Caricare Auto da CSV – Esempio Completo

4

Pulsante “Carica da CSV”

Questo codice legge il file CSV e ricrea gli oggetti Automobile!

Form1.cs – Caricamento Completo da CSV
// ═══════════════════════════════════════════════
// EVENTO: Carica auto da file CSV
// ═══════════════════════════════════════════════
private void btnCaricaCSV_Click(object sender, EventArgs e)
{
    // STEP 1: Controlla se il file esiste
    if (!File.Exists("automobile.csv"))
    {
        MessageBox.Show("File automobile.csv non trovato!\n" +
                      "Salva prima alcune auto con il pulsante 'Salva CSV'.",
                      "File Non Trovato",
                      MessageBoxButtons.OK,
                      MessageBoxIcon.Warning);
        return;
    }

    try
    {
        // STEP 2: Pulisci la lista attuale
        // (rimuovi tutte le auto che ci sono ora)
        listaAuto.Clear();
        
        int autoCaricate = 0;   // Contatore auto caricate
        int rigaCorrente = 0;   // Numero riga attuale
        
        // STEP 3: Apri il file in lettura
        using (StreamReader lettore = new StreamReader("automobile.csv"))
        {
            // STEP 4: Salta la prima riga (intestazione)
            // Prima riga: "Modello;Cilindrata;Carburante;Prezzo"
            if (!lettore.EndOfStream)
            {
                string intestazione = lettore.ReadLine();
                rigaCorrente++;
                // Non usiamo questa riga, era solo l'intestazione
            }
            
            // STEP 5: Leggi tutte le altre righe (i dati veri)
            while (!lettore.EndOfStream)
            {
                rigaCorrente++;
                string riga = lettore.ReadLine();
                
                // STEP 6: Salta righe vuote
                if (string.IsNullOrWhiteSpace(riga))
                    continue;
                
                try
                {
                    // STEP 7: Dividi la riga in parti con Split
                    // Es: "Fiat 500;1200;Benzina;15000"
                    string[] parti = riga.Split(';');
                    
                    // STEP 8: Controlla che ci siano esattamente 4 parti
                    if (parti.Length != 4)
                    {
                        MessageBox.Show($"Riga {rigaCorrente} non valida: {riga}",
                                      "Attenzione",
                                      MessageBoxButtons.OK,
                                      MessageBoxIcon.Warning);
                        continue;  // Salta questa riga e vai alla prossima
                    }
                    
                    // STEP 9: Estrai i dati dalle parti
                    string modello = parti[0];               // "Fiat 500"
                    int cilindrata = Convert.ToInt32(parti[1]);      // 1200
                    string carburante = parti[2];            // "Benzina"
                    double prezzo = Convert.ToDouble(parti[3]);      // 15000.0
                    
                    // STEP 10: Crea un nuovo oggetto Automobile
                    Automobile auto = new Automobile(modello, cilindrata, 
                                                    carburante, prezzo);
                    
                    // STEP 11: Aggiungi l'auto alla lista
                    listaAuto.Add(auto);
                    autoCaricate++;
                }
                catch (FormatException)
                {
                    // Errore di conversione (es: "ABC" invece di un numero)
                    MessageBox.Show($"Errore riga {rigaCorrente}: dati non validi",
                                  "Errore Formato",
                                  MessageBoxButtons.OK,
                                  MessageBoxIcon.Error);
                }
                catch (Exception ex)
                {
                    // Altri errori imprevisti
                    MessageBox.Show($"Errore riga {rigaCorrente}: {ex.Message}",
                                  "Errore",
                                  MessageBoxButtons.OK,
                                  MessageBoxIcon.Error);
                }
            }
        } // ← Il file si chiude automaticamente qui
        
        // STEP 12: Aggiorna la visualizzazione
        AggiornaListaAuto();
        AggiornaStatistiche();
        
        // STEP 13: Mostra messaggio di successo
        MessageBox.Show($"✅ Caricate {autoCaricate} auto con successo!",
                      "Caricamento Completato",
                      MessageBoxButtons.OK,
                      MessageBoxIcon.Information);
    }
    catch (Exception ex)
    {
        MessageBox.Show($"Errore durante il caricamento: {ex.Message}",
                      "Errore",
                      MessageBoxButtons.OK,
                      MessageBoxIcon.Error);
    }
}

🔍 Split() – Dividere una Stringa

// Riga del CSV (è una STRINGA unica):
string riga = "Fiat 500;1200;Benzina;15000";

// Split() la divide in parti usando ';' come separatore
string[] parti = riga.Split(';');

// Risultato - array con 4 elementi:
// parti[0] = "Fiat 500"    ← Modello
// parti[1] = "1200"        ← Cilindrata (stringa!)
// parti[2] = "Benzina"     ← Carburante
// parti[3] = "15000"       ← Prezzo (stringa!)

// Attenzione: parti[1] e parti[3] sono STRINGHE!
// Devi convertirle:
int cilindrata = Convert.ToInt32(parti[1]);    // "1200" → 1200
double prezzo = Convert.ToDouble(parti[3]);    // "15000" → 15000.0

🚀 Caricamento Automatico all’Avvio

5

Caricare i Dati Quando il Programma Si Apre

Il modo migliore è caricare automaticamente i dati nel costruttore del Form!
Così l’utente ritrova subito le sue auto. 😊

Form1.cs – Caricamento Automatico nel Costruttore
public partial class Form1 : Form
{
    private List<Automobile> listaAuto;
    private int indiceModifica = -1;

    public Form1()
    {
        InitializeComponent();
        
        // Inizializza la lista vuota
        listaAuto = new List<Automobile>();
        
        // Popola la ComboBox con i carburanti
        InizializzaCarburanti();
        
        // ═══════════════════════════════════════════════
        // CARICA I DATI AUTOMATICAMENTE ALL'AVVIO
        // ═══════════════════════════════════════════════
        CaricaDatiDaFile();
    }
    
    // ═══════════════════════════════════════════════
    // Metodo per caricare i dati all'avvio
    // ═══════════════════════════════════════════════
    private void CaricaDatiDaFile()
    {
        // Se il file non esiste, esci silenziosamente
        // (è la prima volta che usi il programma)
        if (!File.Exists("automobile.csv"))
        {
            return;  // Lista rimane vuota
        }

        try
        {
            using (StreamReader lettore = new StreamReader("automobile.csv"))
            {
                // Salta intestazione
                if (!lettore.EndOfStream)
                {
                    lettore.ReadLine();
                }
                
                // Carica tutte le auto
                while (!lettore.EndOfStream)
                {
                    string riga = lettore.ReadLine();
                    
                    // Salta righe vuote
                    if (string.IsNullOrWhiteSpace(riga))
                        continue;
                    
                    try
                    {
                        string[] parti = riga.Split(';');
                        
                        // Verifica che ci siano 4 parti
                        if (parti.Length == 4)
                        {
                            string modello = parti[0];
                            int cilindrata = Convert.ToInt32(parti[1]);
                            string carburante = parti[2];
                            double prezzo = Convert.ToDouble(parti[3]);
                            
                            Automobile auto = new Automobile(modello, cilindrata, 
                                                            carburante, prezzo);
                            listaAuto.Add(auto);
                        }
                    }
                    catch
                    {
                        // Salta righe con errori (continua con le altre)
                        continue;
                    }
                }
            }
            
            // Aggiorna la visualizzazione
            AggiornaListaAuto();
            AggiornaStatistiche();
        }
        catch
        {
            // Se c'è un errore generale, ignora
            // (lista rimane vuota, non crashare il programma)
        }
    }
}

✅ Vantaggi del Caricamento Automatico

  • 🚀 Automatico: L’utente non deve cliccare nessun pulsante
  • 💾 Persistenza: I dati sopravvivono alla chiusura del programma
  • 👤 User-Friendly: Esperienza fluida e professionale
  • 🔒 Sicuro: Se il file non esiste o ha errori, non crasha

⚠️ Gestione Errori – Problemi Comuni

🐛 Errori Comuni nella Lettura File

1️⃣ Errore: File Non Trovato

// ❌ SBAGLIATO - Non controlli se esiste
StreamReader lettore = new StreamReader("file_che_non_esiste.csv");
// 💥 CRASH! FileNotFoundException

// ✅ CORRETTO - Controlla sempre prima
if (File.Exists("automobile.csv"))
{
    // OK, il file esiste, puoi leggerlo
    StreamReader lettore = new StreamReader("automobile.csv");
}
else
{
    MessageBox.Show("File non trovato!");
    return;
}

2️⃣ Errore: Formato CSV Sbagliato

// File corrotto:
// "Fiat 500;1200;Benzina"  ← Manca il prezzo!

string[] parti = riga.Split(';');
// parti.Length = 3 (non 4!)

double prezzo = Convert.ToDouble(parti[3]);  
// 💥 CRASH! parti[3] non esiste!

// ✅ SOLUZIONE: Controlla la lunghezza
if (parti.Length != 4)
{
    MessageBox.Show($"Riga non valida: {riga}");
    continue;  // Salta questa riga
}

// Ora è sicuro accedere a parti[3]
double prezzo = Convert.ToDouble(parti[3]);

3️⃣ Errore: Conversione Fallita

// File con errore:
// "Fiat 500;ABC;Benzina;15000"  ← "ABC" non è un numero!

int cilindrata = Convert.ToInt32("ABC");  
// 💥 CRASH! FormatException

// ✅ SOLUZIONE: Usa try-catch
try
{
    int cilindrata = Convert.ToInt32(parti[1]);
    double prezzo = Convert.ToDouble(parti[3]);
    
    // Se arrivi qui, le conversioni sono OK
    Automobile auto = new Automobile(modello, cilindrata, carburante, prezzo);
    listaAuto.Add(auto);
}
catch (FormatException)
{
    MessageBox.Show($"Dato non valido nella riga: {riga}");
    continue;  // Salta questa riga, vai alla prossima
}
🎯 Riepilogo – StreamWriter vs StreamReader
Aspetto StreamWriter
(Scrittura)
StreamReader
(Lettura)
Scopo 📝 Scrivere dati 📖 Leggere dati
Creazione new StreamWriter("file.txt") new StreamReader("file.txt")
Metodi WriteLine()
Write()
ReadLine()
ReadToEnd()
EndOfStream
File Esiste? Lo crea se non esiste ❌ Errore se non esiste
Usa File.Exists()
Quando Salvare dati su disco Caricare dati da disco
Esempio Pulsante “Salva” Avvio programma o pulsante “Carica”

💡 Best Practice per File I/O

  1. Usa sempre using: Garantisce chiusura automatica del file
  2. Controlla File.Exists(): Prima di leggere, verifica che il file esista
  3. Gestisci gli errori: Usa try-catch per file corrotti o dati non validi
  4. Valida i dati: Controlla lunghezza array, conversioni, campi vuoti
  5. Carica all’avvio: Migliora l’esperienza utente caricando automaticamente nel costruttore
  6. Salva spesso: Dopo ogni modifica importante (aggiungi, modifica, rimuovi)
  7. Salta righe problematiche: Con continue invece di far crashare tutto

🔄 Ordinare le Auto – Metodo Semplice

Vuoi vedere le auto in ordine alfabetico o per cilindrata? Ecco come fare in modo semplice!

1

Aggiungi un Pulsante per Ordinare

  • Trascina un Button nel form
  • Chiamalo btnOrdina
  • Imposta la proprietà Text su “Ordina per Modello”

2

Codice per Ordinare

Ordinamento Alfabetico
// ═══════════════════════════════════════
// EVENTO: Ordina le auto per modello
// ═══════════════════════════════════════
private void btnOrdina_Click(object sender, EventArgs e)
{
    // Controlla se ci sono auto da ordinare
    if (listaAuto.Count == 0)
    {
        MessageBox.Show("Non ci sono auto da ordinare!",
                      "Lista Vuota",
                      MessageBoxButtons.OK,
                      MessageBoxIcon.Information);
        return;
    }
    
    // Ordina la lista in ordine alfabetico per modello
    // Sort() usa il metodo CompareTo per confrontare le stringhe
    listaAuto.Sort((auto1, auto2) => auto1.Modello.CompareTo(auto2.Modello));
    
    // Aggiorna la visualizzazione
    AggiornaListaAuto();
    
    // Mostra messaggio
    MessageBox.Show("Auto ordinate per modello in ordine alfabetico!",
                  "Ordinamento Completato",
                  MessageBoxButtons.OK,
                  MessageBoxIcon.Information);
}
🔍 Spiegazione del Codice di Ordinamento

Cosa fa questa riga?

listaAuto.Sort((auto1, auto2) => auto1.Modello.CompareTo(auto2.Modello));

È più semplice di quanto sembri! Funziona così:

  1. Sort() prende le auto due alla volta
  2. Confronta i loro modelli con CompareTo
  3. Se auto1 viene prima di auto2 alfabeticamente, le lascia in quest’ordine
  4. Altrimenti le scambia
  5. Ripete fino a quando tutte le auto sono in ordine

💡 Esempio Visuale:

PRIMA dell'ordinamento:
[0] Tesla Model 3
[1] Fiat 500
[2] BMW Serie 3

DOPO l'ordinamento:
[0] BMW Serie 3      ← Viene prima alfabeticamente
[1] Fiat 500
[2] Tesla Model 3

🎯 Vuoi Ordinare per Cilindrata?

Cambia semplicemente la riga così:

// Ordina per cilindrata (dal più piccolo al più grande)
listaAuto.Sort((auto1, auto2) => auto1.Cilindrata.CompareTo(auto2.Cilindrata));




🧬 Ereditarietà: Le Classi Figlie

Hai creato la classe Automobile. Ma cosa succede se vuoi gestire anche
AutoElettrica o AutoSportiva?
Hanno molte cose in comune con Automobile, ma anche caratteristiche diverse.
Devi riscrivere tutto da capo? No! Esiste l’ereditarietà.

🤔 Il Problema Senza Ereditarietà

❌ SENZA Ereditarietà – Codice Duplicato
// Devi scrivere due classi SEPARATE con tutto il codice ripetuto
class Automobile
{
    private string _modello;
    private int _cilindrata;
    private string _carburante;
    // ... costruttore, proprietà, metodi ...
}

class AutoElettrica
{
    private string _modello;       // ← DUPLICATO!
    private int _cilindrata;       // ← DUPLICATO!
    private string _carburante;    // ← DUPLICATO!
    private int _autonomiaKm;      // Nuovo campo specifico
    // ... tutto il costruttore duplicato ...
    // ... tutte le proprietà duplicate ...
}

// 😱 Problemi:
// 1. Codice identico scritto due volte → errori facilissimi
// 2. Se devi correggere un bug, devi farlo in ENTRAMBE le classi
// 3. Non è scalabile: per 10 tipi di auto, 10 classi duplicate!

✅ CON Ereditarietà – Codice Riutilizzato

✅ CON Ereditarietà – DRY (Don’t Repeat Yourself)
// La classe BASE ha tutto il codice comune
class Automobile
{
    private string _modello;
    private int _cilindrata;
    private string _carburante;
    // ... proprietà e metodi comuni a TUTTE le auto ...
}

// La classe FIGLIA eredita tutto e aggiunge solo il nuovo
class AutoElettrica : Automobile   // ← Il ":" significa "estende"
{
    private int _autonomiaKm;      // Solo la parte NUOVA!
    // tutto il resto lo prende GRATIS da Automobile!
}

// ✅ Vantaggi:
// 1. AutoElettrica ha già Modello, Cilindrata, Carburante
// 2. Correggi un bug in Automobile → fix automatico per tutti
// 3. Aggiungi 10 tipi di auto senza duplicare nulla
🎯 Analogia del Mondo Reale

Pensa alla biologia:

  • Animale (classe base) → respira, si muove, ha un nome
  • Cane (classe figlia) → eredita tutto da Animale + abbaia, ha una razza
  • Gatto (classe figlia) → eredita tutto da Animale + fa le fusa, è indipendente

Il Cane non deve “reimparare” a respirare: lo eredita dall’Animale!

              Automobile (CLASSE BASE / GENITORE)
              ├── Modello
              ├── Cilindrata
              ├── Carburante
              └── ToString()
                      │
          ┌───────────┼───────────┐
          ↓           ↓           ↓
    AutoElettrica  AutoSportiva  AutoLusso
    (FIGLIA)       (FIGLIA)      (FIGLIA)
    + AutonomiaKm  + Cavalli     + Prezzo
    + Batteria     + Turbo       + Brand

📝 Come Si Scrive una Classe Figlia

1

Modificare la Classe Base: aggiungere protected

Prima di creare classi figlie, devi modificare la classe Automobile.
Le classi figlie non possono accedere ai campi private del genitore.
Devi usare protected:

Automobile.cs – Campi diventano protected
public class Automobile
{
    // ─────────────────────────────────────────────────────────
    // PRIMA usavi "private" → SOLO questa classe poteva vederli
    // ORA usi "protected" → anche le classi FIGLIE possono vederli
    // Dall'esterno (Form, altri) rimangono comunque nascosti!
    // ─────────────────────────────────────────────────────────
    protected string _modello;        // ← protected, non private
    protected int _cilindrata;        // ← protected, non private
    protected string _carburante;     // ← protected, non private

    // Le PROPRIETÀ e i METODI pubblici rimangono uguali
    public string Modello
    {
        get { return _modello; }
        set { if (!string.IsNullOrEmpty(value)) _modello = value; }
    }
    public int Cilindrata
    {
        get { return _cilindrata; }
        set { if (value >= 500 && value <= 10000) _cilindrata = value; }
    }
    public string Carburante
    {
        get { return _carburante; }
        set { if (!string.IsNullOrEmpty(value)) _carburante = value; }
    }

    // Il costruttore rimane uguale
    public Automobile(string modello, int cilindrata, string carburante)
    {
        Modello = modello;
        Cilindrata = cilindrata;
        Carburante = carburante;
    }

    // ─────────────────────────────────────────────────────────
    // IMPORTANTE: aggiungi "virtual" ai metodi che le figlie
    // potranno personalizzare (override)
    // ─────────────────────────────────────────────────────────
    public virtual string Stampa()
    {
        return $"{Modello} - {Cilindrata}cc - {Carburante}";
    }

    public override string ToString()
    {
        return Stampa();
    }
}

🔑 I Tre Livelli di Visibilità

Parola chiave Chi può accedere? Quando usarla
private Solo la classe stessa Se non hai classi figlie
protected La classe + le classi figlie Quando usi l’ereditarietà ✅
public Tutti (Form, altre classi…) Proprietà e metodi pubblici

2

Creare la Classe Figlia: AutoElettrica

Aggiungi un nuovo file AutoElettrica.cs al progetto (come hai fatto per Automobile.cs).

AutoElettrica.cs – Classe Figlia Completa
using System;
using System.Windows.Forms;

namespace ParcoAuto
{
    // ═══════════════════════════════════════════════════
    //  AutoElettrica ESTENDE Automobile
    //  Il ":" indica "eredita da"
    //  Leggi: "AutoElettrica è un tipo di Automobile"
    // ═══════════════════════════════════════════════════
    public class AutoElettrica : Automobile
    {
        // ─────────────────────────────────────────────
        // CAMPI PRIVATI propri di AutoElettrica
        // (quelli di Automobile li ha già ereditati!)
        // ─────────────────────────────────────────────
        private int _autonomiaKm;
        private int _percentualeBatteria;

        // ─────────────────────────────────────────────
        // PROPRIETÀ proprie di AutoElettrica
        // ─────────────────────────────────────────────
        public int AutonomiaKm
        {
            get { return _autonomiaKm; }
            set
            {
                if (value >= 50 && value <= 1000) { _autonomiaKm = value; } else { MessageBox.Show("Autonomia deve essere tra 50 e 1000 km!", "ERRORE", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } public int PercentualeBatteria { get { return _percentualeBatteria; } set { if (value >= 0 && value <= 100)
                    _percentualeBatteria = value;
                else
                    MessageBox.Show("Percentuale batteria deve essere tra 0 e 100!",
                                  "ERRORE", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // ─────────────────────────────────────────────
        // PROPRIETÀ CALCOLATA: km rimasti con la carica attuale
        // ─────────────────────────────────────────────
        public double KmRimasti
        {
            get { return _autonomiaKm * (_percentualeBatteria / 100.0); }
        }

        // ─────────────────────────────────────────────
        // COSTRUTTORE della classe figlia
        // Deve chiamare il costruttore del GENITORE con "base(...)"
        // ─────────────────────────────────────────────
        public AutoElettrica(string modello, int autonomiaKm, int percentualeBatteria)
            : base(modello, 0, "Elettrica")   // ← chiama Automobile(modello, 0, "Elettrica")
        {
            // Dopo che il costruttore base ha fatto il suo lavoro,
            // impostiamo le proprietà specifiche di AutoElettrica
            AutonomiaKm = autonomiaKm;
            PercentualeBatteria = percentualeBatteria;
        }

        // ─────────────────────────────────────────────
        // OVERRIDE di Stampa() → versione personalizzata
        // Sostituisce la versione del genitore
        // ─────────────────────────────────────────────
        public override string Stampa()
        {
            return $"{Modello} [⚡ Elettrica] - Autonomia: {AutonomiaKm}km - " +
                   $"Batteria: {PercentualeBatteria}% ({KmRimasti:F0}km rimasti)";
        }
        // ToString() NON serve riscriverlo! Chiama già Stampa()
        // che ora è la versione override di AutoElettrica ✅
    }
}

🔍 Analisi del Costruttore Figlia

public AutoElettrica(string modello, int autonomiaKm, int percentualeBatteria)
    : base(modello, 0, "Elettrica")
//  ↑
//  "base(...)" = chiama il costruttore di AUTOMOBILE
//  Gli passo:  modello (dall'utente), 0 (cilindrata 0 per elettrica),
//              "Elettrica" (carburante fisso)
//
// ORDINE di esecuzione:
// 1. Prima viene eseguito il costruttore di Automobile (base)
//    → imposta _modello, _cilindrata, _carburante
// 2. Poi viene eseguito il corpo di AutoElettrica
//    → imposta _autonomiaKm, _percentualeBatteria

3

Creare una Seconda Figlia: AutoSportiva

AutoSportiva.cs
namespace ParcoAuto
{
    public class AutoSportiva : Automobile
    {
        private int _cavalliVapore;
        private bool _hasTurbo;

        public int CavalliVapore
        {
            get { return _cavalliVapore; }
            set
            {
                if (value >= 100 && value <= 2000)
                    _cavalliVapore = value;
                else
                    MessageBox.Show("Cavalli vapore devono essere tra 100 e 2000!",
                                  "ERRORE", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        public bool HasTurbo
        {
            get { return _hasTurbo; }
            set { _hasTurbo = value; }
        }

        // Costruttore: chiama base per modello, cilindrata, carburante
        public AutoSportiva(string modello, int cilindrata, string carburante,
                            int cavalliVapore, bool hasTurbo)
            : base(modello, cilindrata, carburante)
        {
            CavalliVapore = cavalliVapore;
            HasTurbo = hasTurbo;
        }

        // Override personalizzato per AutoSportiva
        public override string Stampa()
        {
            string turbo = HasTurbo ? " [TURBO]" : "";
            return $"{Modello} [🏎️ Sport]{turbo} - {Cilindrata}cc - " +
                   $"{CavalliVapore}cv - {Carburante}";
        }
    }
}

🔁 Override: Personalizzare i Metodi Ereditati

override significa letteralmente “sostituire”.
Una classe figlia può riscrivere un metodo del genitore per dargli un comportamento diverso,
più adatto al suo tipo specifico.

🔑 Le Tre Parole Chiave: virtual, override, base

Parola chiave Dove si mette Cosa significa
virtual Nel metodo della classe BASE “Questo metodo può essere riscritto dalle figlie”
override Nel metodo della classe FIGLIA “Sostituisco la versione del genitore con la mia”
base.Metodo() Dentro l’override della FIGLIA “Richiama anche il metodo originale del genitore”
Esempio completo – virtual, override, base
// ─────────────────────────────────────────────────
// CLASSE BASE
// ─────────────────────────────────────────────────
public class Automobile
{
    public virtual string Stampa()          // ← virtual: "può essere overridato"
    {
        return $"{Modello} - {Cilindrata}cc - {Carburante}";
    }
}

// ─────────────────────────────────────────────────
// CLASSE FIGLIA - Override completo
// ─────────────────────────────────────────────────
public class AutoElettrica : Automobile
{
    public override string Stampa()        // ← override: "sostituisco la versione base"
    {
        // Versione completamente nuova, ignora quella di Automobile
        return $"{Modello} [⚡] - Autonomia: {AutonomiaKm}km";
    }
}

// ─────────────────────────────────────────────────
// CLASSE FIGLIA - Override con base (estende senza perdere)
// ─────────────────────────────────────────────────
public class AutoSportiva : Automobile
{
    public override string Stampa()
    {
        // Richiama il metodo del genitore e CI AGGIUNGE qualcosa
        string testoBase = base.Stampa();  // ← "base." chiama Automobile.Stampa()
        // testoBase = "Ferrari 488 - 3900cc - Benzina"
        
        return testoBase + $" | {CavalliVapore}cv";
        // Risultato: "Ferrari 488 - 3900cc - Benzina | 660cv"
    }
}

// ─────────────────────────────────────────────────
// Risultato nelle listbox:
// ─────────────────────────────────────────────────
// Automobile normale:  "Fiat 500 - 1200cc - Benzina"
// AutoElettrica:       "Tesla [⚡] - Autonomia: 500km"
// AutoSportiva:        "Ferrari 488 - 3900cc - Benzina | 660cv"
⚠️ Errore Comune: dimenticare “virtual”
// ❌ SBAGLIATO: metodo nella base senza "virtual"
public class Automobile
{
    public string Stampa()   // ← manca "virtual"!
    {
        return $"{Modello} - {Cilindrata}cc";
    }
}

public class AutoElettrica : Automobile
{
    public override string Stampa()   // 💥 ERRORE di compilazione!
    // "non si può fare override di un metodo non-virtual"
    { ... }
}

// ✅ SOLUZIONE: aggiungi "virtual" nella classe base
public class Automobile
{
    public virtual string Stampa()   // ← ora le figlie possono overridarlo
    { ... }
}

🎭 Polimorfismo: Un’Unica Lista per Tutti i Tipi

Polimorfismo viene dal greco: “molte forme”.
È il concetto più potente dell’ereditarietà: puoi trattare oggetti di tipo diverso
come se fossero tutti dello stesso tipo base.

🤔 Il Problema: Tre Liste Separate

❌ SENZA Polimorfismo – Devi gestire tre liste
// Devi tenere tre liste separate:
private List<Automobile>   listaAuto;
private List<AutoElettrica> listaElettriche;
private List<AutoSportiva>  listaSportive;

// Tre foreach per aggiornare la ListBox:
foreach (Automobile a in listaAuto)       lstAuto.Items.Add(a);
foreach (AutoElettrica e in listaElettriche) lstAuto.Items.Add(e);
foreach (AutoSportiva s in listaSportive)  lstAuto.Items.Add(s);

// Tre cicli per salvare su file:
// ... idem ...

// 😱 Il codice esplode di complessità!

✅ CON Polimorfismo – Una sola lista, tutto funziona

✅ CON Polimorfismo – Elegante e semplice
// UNA SOLA lista di tipo Automobile
// ma può contenere anche AutoElettrica e AutoSportiva!
// Perché AutoElettrica e AutoSportiva SONO Automobile (ereditarietà)
private List<Automobile> listaAuto;

// Aggiungi tutti i tipi nella stessa lista:
listaAuto.Add(new Automobile("Fiat 500", 1200, "Benzina"));
listaAuto.Add(new AutoElettrica("Tesla Model 3", 500, 85));
listaAuto.Add(new AutoSportiva("Ferrari 488", 3900, "Benzina", 660, true));

// UN SOLO foreach per aggiornarla:
foreach (Automobile auto in listaAuto)
{
    // C# chiama automaticamente il Stampa() GIUSTO per ogni oggetto!
    // - Se è Automobile normale → usa Automobile.Stampa()
    // - Se è AutoElettrica     → usa AutoElettrica.Stampa() (override)
    // - Se è AutoSportiva      → usa AutoSportiva.Stampa()  (override)
    lstAuto.Items.Add(auto);   // ToString() → Stampa() corretto!
}

// Risultato nella ListBox:
// Fiat 500 - 1200cc - Benzina
// Tesla Model 3 [⚡ Elettrica] - Autonomia: 500km - Batteria: 85%
// Ferrari 488 [🏎️ Sport][TURBO] - 3900cc - 660cv - Benzina

🎬 Come Funziona il Polimorfismo – Dietro le Quinte

// La lista contiene oggetti di tipo diverso, MA la variabile è Automobile:
Automobile auto = listaAuto[1];   // Prendo il secondo elemento

// Questo oggetto è in realtà un'AutoElettrica (era il Tesla)
// Ma C# SA che tipo è realmente (lo ricorda in memoria)

// Quando chiami Stampa()...
string testo = auto.Stampa();
//             ↑
// C# non chiama Automobile.Stampa()
// C# chiama AutoElettrica.Stampa()  ← quello giusto!
//
// Questo si chiama DISPATCH DINAMICO:
// la versione del metodo da chiamare viene scelta
// a RUNTIME (mentre il programma gira) non a compilazione

// È come se l'oggetto dicesse:
// "Sì, mi chiami Automobile, ma io so CHI SONO davvero!"

🔎 Riconoscere il Tipo Reale: is e as

A volte hai bisogno di sapere il tipo reale di un oggetto nella lista e accedere alle proprietà specifiche.

foreach (Automobile auto in listaAuto)
{
    // "is" controlla se l'oggetto è di quel tipo
    if (auto is AutoElettrica)
    {
        // "as" converte l'oggetto al tipo specifico
        AutoElettrica elettrica = auto as AutoElettrica;
        
        // Ora puoi accedere alle proprietà di AutoElettrica
        MessageBox.Show($"Autonomia: {elettrica.AutonomiaKm}km");
    }
    else if (auto is AutoSportiva)
    {
        AutoSportiva sportiva = auto as AutoSportiva;
        MessageBox.Show($"Potenza: {sportiva.CavalliVapore}cv");
    }
    else
    {
        // È un'Automobile normale
        MessageBox.Show($"Auto standard: {auto.Modello}");
    }
}

🛠️ Esempio Pratico: Aggiungere le Classi Figlie al Form

Vediamo come modificare il Form del Parco Auto per gestire anche
AutoElettrica e AutoSportiva con la stessa lista.

1

Aggiungere una ComboBox per il Tipo di Auto

Aggiungi una ComboBox chiamata cmbTipoAuto con le voci:
“Standard”, “Elettrica”, “Sportiva”.

Form1.cs – Inizializzazione
private void InizializzaTipiAuto()
{
    cmbTipoAuto.Items.Add("Standard");
    cmbTipoAuto.Items.Add("Elettrica");
    cmbTipoAuto.Items.Add("Sportiva");
    cmbTipoAuto.SelectedIndex = 0;
}

2

Logica di Aggiunta: creare il tipo giusto in base alla scelta

Form1.cs – btnAggiungi_Click con Polimorfismo
private void btnAggiungi_Click(object sender, EventArgs e)
{
    if (!ControllaCampi()) return;
    if (!ValidaCilindrata(txtCilindrata.Text)) return;

    string modello    = txtModello.Text;
    int cilindrata    = Convert.ToInt32(txtCilindrata.Text);
    string carburante = cmbCarburante.SelectedItem.ToString();
    string tipoScelto = cmbTipoAuto.SelectedItem.ToString();

    // Variabile di tipo BASE → può contenere qualsiasi figlia
    Automobile nuovaAuto;

    // Crea il tipo giusto in base alla scelta dell'utente
    if (tipoScelto == "Elettrica")
    {
        // Chiedi i dati specifici per l'elettrica
        int autonomia = 400;     // Potresti leggerli da una TextBox dedicata
        int batteria = 100;
        nuovaAuto = new AutoElettrica(modello, autonomia, batteria);
    }
    else if (tipoScelto == "Sportiva")
    {
        int cv = 300;            // Anche questi da TextBox dedicate
        bool turbo = false;
        nuovaAuto = new AutoSportiva(modello, cilindrata, carburante, cv, turbo);
    }
    else
    {
        // Standard
        nuovaAuto = new Automobile(modello, cilindrata, carburante);
    }

    // Aggiunge alla lista (polimorfismo: sempre List<Automobile>!)
    listaAuto.Add(nuovaAuto);

    // Aggiorna - funziona per TUTTI i tipi grazie al polimorfismo
    AggiornaListaAuto();
    AggiornaStatistiche();
    PulisciCampi();
}

3

Statistiche per Tipo – Usare is per contare

Form1.cs – AggiornaStatistiche con tipi
private void AggiornaStatistiche()
{
    int totale      = listaAuto.Count;
    int standard    = 0;
    int elettriche  = 0;
    int sportive    = 0;

    foreach (Automobile auto in listaAuto)
    {
        if (auto is AutoElettrica) elettriche++;
        else if (auto is AutoSportiva) sportive++;
        else standard++;
    }

    lblTotaleAuto.Text = $"Totale: {totale} " +
                         $"(Standard: {standard} | " +
                         $"⚡ Elettriche: {elettriche} | " +
                         $"🏎️ Sportive: {sportive})";
}

📊 Schema Riassuntivo: Ereditarietà e Polimorfismo

┌─────────────────────────────────────────────────────────────────┐
│                    AUTOMOBILE (Classe Base)                     │
│  protected: _modello, _cilindrata, _carburante                  │
│  public:    Modello, Cilindrata, Carburante (proprietà)         │
│  virtual:   Stampa()  ← può essere sostituito dalle figlie      │
└──────────────────────────┬──────────────────────────────────────┘
                           │  eredita con ":"
             ┌─────────────┼─────────────┐
             ↓             ↓             ↓
   ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
   │AutoElettrica │ │ AutoSportiva │ │  AutoLusso   │
   │ (figlia)     │ │  (figlia)    │ │  (figlia)    │
   │              │ │              │ │              │
   │ +AutonomiaKm │ │ +CavalliVap. │ │ +Prezzo      │
   │ +Batteria%   │ │ +HasTurbo    │ │ +Brand       │
   │ +KmRimasti   │ │              │ │              │
   │              │ │              │ │              │
   │ override     │ │ override     │ │ override     │
   │ Stampa()     │ │ Stampa()     │ │ Stampa()     │
   └──────────────┘ └──────────────┘ └──────────────┘

POLIMORFISMO in azione:
  List<Automobile> lista = new List<Automobile>();
  lista.Add(new Automobile(...));      // ✅ Standard
  lista.Add(new AutoElettrica(...));   // ✅ Figlia accettata!
  lista.Add(new AutoSportiva(...));    // ✅ Figlia accettata!

  foreach (Automobile a in lista)
  {
      lstAuto.Items.Add(a);   // Chiama Stampa() GIUSTO per ognuno ✅
  }

REGOLA FONDAMENTALE:
  "Una figlia PUÒ essere usata dove si aspetta il genitore"
  AutoElettrica IS-A Automobile  → true ✅
  Automobile IS-A AutoElettrica  → false ❌

🎯 Riepilogo: Tre Concetti in Tre Frasi

🧬 Ereditarietà

La classe figlia riceve gratis tutto quello che ha la classe genitore.
Aggiungi solo le parti nuove. Si scrive con : NomeGenitore.

class AutoElettrica : Automobile
{ /* eredita tutto! */ }

🔁 Override

La figlia può riscrivere un metodo del genitore per adattarlo al suo caso.
Il genitore marca il metodo virtual, la figlia usa override.

public override string Stampa()
{ return "versione mia!"; }

🎭 Polimorfismo

Puoi mettere tipi diversi nella stessa lista (di tipo base).
Ogni oggetto risponde con il metodo giusto per il suo tipo.

List<Automobile> lista;
lista.Add(new AutoElettrica(...));
// funziona! ✅

🎓 Riepilogo: Cosa Hai Imparato

✨ Un Grande Passo Avanti nella Programmazione!

1️⃣ Classi con Campi Privati

Hai imparato il metodo professionale: private _campo + proprietà pubblica con validazione.
Ora i tuoi dati sono protetti!

2️⃣ Liste di Oggetti

Da List<string> a List<Automobile>: gestisci strutture dati complesse come un professionista!

3️⃣ Operazioni CRUD

Create (aggiungi), Read (visualizza), Update (modifica), Delete (rimuovi):
tutte le operazioni base di un database!

4️⃣ Ordinamento

Ordina le liste per qualsiasi campo con un solo comando: alfabetico, numerico, come vuoi tu!

5️⃣ Salvataggio su File

StreamWriter per salvare su TXT e CSV: i tuoi dati sopravvivono alla chiusura del programma!

6️⃣ Validazione Dati

Controlli sui valori inseriti, messaggi di errore chiari: il tuo programma è robusto e professionale!

7️⃣ Ereditarietà

Le classi figlie ricevono gratis tutto il codice del genitore con : NomeClasse.
Scrivi meno, riusa di più: niente più codice duplicato!

8️⃣ Override

Con virtual nel genitore e override nella figlia, ogni classe può
personalizzare un metodo mantenendo la stessa firma. Puoi anche richiamare l’originale con base.Metodo().

9️⃣ Polimorfismo

Una sola List<Automobile> può contenere Automobile, AutoElettrica, AutoSportiva.
Ogni oggetto risponde con il metodo giusto per il suo tipo, automaticamente.

🏆 Ora Sei un Programmatore Vero!

Con questa lezione hai imparato concetti fondamentali della programmazione professionale.
Non stai più solo “giocando” con i controlli: ora sai progettare strutture dati,
proteggere le informazioni, rendere permanenti i dati e costruire gerarchie di classi
con ereditarietà e polimorfismo. Queste competenze sono la base per qualsiasi applicazione seria!

💪 Esercizi per Fare Pratica

🎯 Prova a Fare Questi Progetti:

1️⃣ Gestionale Contatti

  • Crea una classe Contatto con: Nome, Cognome, Telefono, Email
  • Usa campi privati con proprietà
  • Aggiungi validazione: email deve contenere @, telefono solo numeri
  • Salva i contatti in un file TXT
  • Permetti di ordinare per cognome

2️⃣ Registro Voti

  • Crea una classe Studente con: Nome, Materia, Voto
  • Il voto deve essere tra 0 e 10 (validazione nel SET)
  • Calcola e mostra la media dei voti
  • Trova lo studente con il voto più alto
  • Salva tutto in CSV per aprirlo con Excel

3️⃣ Catalogo Libri

  • Crea una classe Libro con: Titolo, Autore, Anno, Prezzo
  • Aggiungi un metodo che calcola l’età del libro
  • Permetti di cercare libri per autore
  • Ordina per prezzo (dal più economico al più costoso)
  • Salva la biblioteca in formato TXT ben formattato

4️⃣ Parco Auto con Ereditarietà Sfida!

  • Parti dal progetto Parco Auto di questa lezione
  • Crea la classe figlia AutoElettrica con AutonomiaKm e PercentualeBatteria
  • Crea la classe figlia AutoSportiva con CavalliVapore e HasTurbo
  • Overrida Stampa() in entrambe per mostrare i dati specifici
  • Usa una sola List<Automobile> per contenere tutti i tipi
  • Nelle statistiche, mostra il conteggio separato per tipo (is)


📱 Livello Base: Device Elettronici con suggerimenti

💡 Suggerimento: Inizia con una classe base Device con marca, modello e prezzo

  • Crea una classe base Device con: Marca, Modello, Prezzo (virtual per calcolo sconto)
  • Crea classi derivate:
    • Smartphone con SistemaOperativo e MemoriaGB
    • Tablet con HasPenna e PolliciSchermo
    • Laptop con RamGB e Processore
  • 🔍 Override del metodo Descrizione() in ogni classe figlia
  • Aggiungi CalcolaPrezzoScontato() nella base e overridalo in Smartphone (sconto 10%) e Laptop (sconto 15%)
  • Usa List<Device> per gestire tutti i dispositivi

💰 Livello Facile: App Pagamenti Digitali

🌍 Ispirato a Satispay, PayPal, carte di credito

  • Classe base MetodoPagamento con: Intestatario, Saldo
  • Classi derivate:
    • CartaCredito: Numero, Scadenza, CVV, Massimale
    • PayPal: Email, CashbackPercentuale
    • AppBancaria: IBAN, Banca, HasBiometricAuth
  • Override di EseguiPagamento(decimal importo):
    • CartaCredito: verifica massimale
    • PayPal: applica cashback se importo > 50€
    • AppBancaria: richiede conferma biometrica
  • 🎯 Extra: Override di CalcolaCostoTransazione() diverso per ogni metodo

🏠 Livello Intermedio: Casa Intelligente

🏷️ Dispositivi smart che comunicano come nella vita reale

  • Classe base astratta SmartDevice con: Nome, Stanza, Stato, Consumo
  • Interfaccia ITimerSupport per dispositivi con timer
  • Classi derivate:
    • LampadinaSmart: ColoreRGB, Luminosità, ITimerSupport
    • Termostato: TemperaturaAttuale, TemperaturaTarget
    • TapparellaElettrica: PercentualeApertura
  • Override obbligatorio di EseguiComando(string comando) per ogni dispositivo
  • Gestisci tutto con List<SmartDevice> (polimorfismo)

🛵 Livello Avanzato: App di Delivery

📦 Ispirato a Glovo/JustEat – diversi tipi di consegna

  • Classe base Ordine con: ID, Cliente, Indirizzo
  • Interfacce:
    • IConsegnaFresco per catena del freddo
    • IConsegnaUrgente per consegne prioritarie
    • ITracciabile per tracking GPS
  • Classi con interfacce multiple:
    • OrdineCibo: TipoCucina, Temperatura → IConsegnaFresco, IConsegnaUrgente
    • OrdineFarmacia: RicettaMedica → IConsegnaUrgente, ITracciabile
    • OrdineRegalo: Messaggio → ITracciabile
  • Override di CalcolaCostoConsegna() con regole diverse per ogni tipo
  • 🏆 Challenge: Sistema di priorità – ordini urgenti processati prima

🎮 Livello Esperto: Personaggi RPG

⚔️ Gerarchia complessa con override a catena

  • Classe base astratta Personaggio con: Nome, Livello, PV, Mana
  • Metodi virtual: Attacca(), Difendi(), UsaAbilita()
  • Gerarchia a 3 livelli:
    • Liv1: Guerriero, Mago, Arciere
    • Liv2: Paladino (da Guerriero), Negromante (da Mago)
    • Liv3: CavaliereDrago (da Paladino)
  • Override a catena con base.Attacca() per accumulare bonus
  • Pattern Observer: quando un personaggio muore, tutti i nemici reagiscono

⭐ BONUS: Gestione Dipendenti Aziendale

Simula ruoli aziendali con diverse regole di stipendio – Confronto tra didattica e realtà

  • Classe base Dipendente: Nome, Cognome, Matricola, Anzianità
  • Classi derivate:
    • Impiegato: Livello, Straordinari (override stipendio con bonus)
    • Manager: BudgetTeam, Progetti (override bonus progetti)
    • Dirigente: StockOptions (override partecipazione utili)
    • Stagista: Tutor, Durata (override stipendio = rimborso spese)
  • Interfaccia IConsulente per chi lavora su progetti esterni
  • Override di ToString() per riepilogo completo

📊 CONFRONTO: Come funziona nel programma vs Come funziona davvero

1️⃣ IMPIEGATO
Aspetto Nel PROGRAMMA Nella VITA REALE
Livello Numero da 1 a 10 che dà 100€ in più al mese per ogni livello È il tuo inquadramento (es. apprendista, impiegato semplice, impiegato senior). Ogni livello ha uno stipendio minimo diverso stabilito dal contratto nazionale
Straordinari 25€ per ogni ora, pagati subito nello stipendio del mese Si pagano con una maggiorazione: +15% se sono in settimana, +30% se di sabato, +50% se di notte. Di solito li pagano il mese dopo
Calcolo base + (livello×100) + (straordinari×25) Minimo del contratto + aumenti per anzianità + straordinari con percentuali

2️⃣ MANAGER
Aspetto Nel PROGRAMMA Nella VITA REALE
Budget Team Prende il 5% del budget TUTTI I MESI Il budget è per tutto l’anno. Il premio (bonus) lo riceve a fine anno se ha raggiunto gli obiettivi, e può essere dal 10% al 50% del suo stipendio annuale
Progetti 500€ per ogni progetto finito, pagati subito I progetti fanno parte degli obiettivi annuali. Non è “tot progetti fatti” ma “come li hai fatti” (rispettato tempi? budget? qualità?)
Esempio Budget 100.000€ → 5.000€ in più ogni mese (60.000€ in più all’anno di soli bonus) Stipendio 50.000€ all’anno + premio 10.000€ a fine anno se obiettivi raggiunti

3️⃣ DIRIGENTE
Aspetto Nel PROGRAMMA Nella VITA REALE
Stock Options È solo un numero, non viene usato per calcolare lo stipendio È il diritto di comprare azioni della società a un prezzo scontato. Se il valore delle azioni sale, puoi rivenderle e guadagnare la differenza
Utile Aziendale Prende il 2% dell’utile TUTTI I MESI L’utile si calcola sul bilancio di fine anno. Il dirigente riceve una percentuale sugli utili una volta all’anno
Esempio Utile 2 milioni → 40.000€ in più ogni mese (480.000€ in più all’anno) Utile 2 milioni → premio di 40.000€ una volta a fine anno

4️⃣ STAGISTA
Aspetto Nel PROGRAMMA Nella VITA REALE
Tutor È solo un nome associato allo stagista È la persona che segue lo stagista, gli insegna il lavoro, firma i documenti e alla fine lo valuta
Durata Mesi di stage (da 1 a 6) Di solito 3 o 6 mesi. A volte si può allungare. La legge dice quanto può durare al massimo
Rimborso 600€ fissi al mese per tutti Se è stage per scuola/università spesso non pagano. Se è stage dopo gli studi, danno un rimborso che può essere 300-800€ in base al contratto

5️⃣ CONSULENZA (chi lavora su progetti esterni)
Aspetto Nel PROGRAMMA Nella VITA REALE
Tariffa oraria Moltiplica per le ore e paga subito Chi fa consulenza di solito ha partita IVA. Emette fattura e viene pagato dopo 30-60-90 giorni, deve pagarci le tasse sopra

⚠️ LE DIFFERENZE PIÙ IMPORTANTI

Concetto Nel programma Nella realtà
Premio per progetti Pagato subito, ogni mese Si valuta a fine anno
Percentuale sul budget 5% tutti i mesi Premio una volta all’anno se raggiungi obiettivi
Percentuale sugli utili 2% tutti i mesi Una volta all’anno a bilancio chiuso
Stock options Solo un numero Possibilità di guadagnare in futuro se le azioni salgono

PROGRAMMA

Il nostro codice è fatto per imparare a programmare, non per fare paghe vere. Serve a capire:

  • Override – Come le classi figlie possono cambiare i metodi del padre
  • Polimorfismo – Lo stesso metodo (CalcolaStipendio()) funziona in modo diverso per ogni tipo
  • Incapsulamento – I dati sono protetti ma accessibili alle classi che servono

Se dovessimo fare un programma vero per paghe, sarebbe molto più complicato: bisognerebbe distinguere mensile da annuale, calcolare tasse, contributi, e mille altre cose. Ma per imparare l’ereditarietà e il polimorfismo, così va benissimo!

📚 Mappa dei Concetti negli Esercizi:

Esercizio Ereditarietà Polimorfismo Override Extra
Device ✅ Base ✅ List ✅ Virtual Suggerimenti
Pagamenti ✅ Singola ✅ Metodi ✅ Con logica Contesto reale
Smart Home ✅ Astratta ✅ Comandi ✅ Obbligatorio Interfacce
Delivery ✅ Multipla ✅ Pattern ✅ Complesso Interfacce multiple
RPG ✅ Multi-livello ✅ Avanzato ✅ A catena Observer
Aziendale ✅ Gerarchica ✅ Oggetti ✅ ToString Mondo reale

🖥️ Suggerimenti per l’Interfaccia Grafica:

  • ComboBox per selezionare il tipo di oggetto da creare
  • Campi che cambiano dinamicamente in base al tipo selezionato
  • DataGridView con colonne specifiche usando polimorfismo
  • Pannello statistiche che conta i tipi con ‘is’
  • Per RPG: barre della vita visive che si aggiornano

🚀 Prossimi Passi

📖 Nella Lezione 6 Imparerai:

  • Eventi Multipli: Un solo metodo per gestire tanti controlli
  • 🎯 Il Parametro sender: Scoprire QUALE controllo ha generato l’evento
  • Validazione in Tempo Reale: Controllare mentre l’utente digita
  • 🎨 Feedback Visivo: Cambiare colori per guidare l’utente

🎯 Scriverai codice più elegante e programmi più user-friendly!

✅ In questa Lezione hai affrontato:

  • 🏗️ Classi con campi private/protected e proprietà con validazione
  • 📋 CRUD completo su List<Automobile>
  • 💾 Salvataggio e lettura da file TXT e CSV
  • 🔄 Ordinamento con Sort() e lambda
  • 🧬 Ereditarietà con classi figlie (: NomeClasse)
  • 🔁 Override di metodi (virtual + override + base)
  • 🎭 Polimorfismo: una lista, tanti tipi, ogni oggetto risponde correttamente

Lezione 5 – Parco Auto: Classi, Ereditarietà e File I/O

Corso C# Completo con Interfaccia Grafica – Da Zero a Esperto

Torna in alto