Lezione 5 – Progetti Avanzati
🚗 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
// 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
// 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
- ✅ 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
- Apri Visual Studio
- Apri il tuo progetto esistente (es: “ParcoAuto”)
STEP 2: Aggiungere una Nuova Classe
- Guarda la finestra Esplora Soluzioni (Solution Explorer) sulla destra
- Fai clic destro sul nome del progetto “ParcoAuto”
- Nel menu che appare, seleziona:
Aggiungi → Nuovo elemento... - Si aprirà una finestra “Aggiungi nuovo elemento”
- Nella lista centrale, seleziona Classe (Class)
- In basso, nel campo Nome, scrivi:
Automobile.cs - 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
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
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!
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!
└─────────────────────────────┘
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!”
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:
- Protezione: Nessuno può mettere valori assurdi nei tuoi dati
- Controllo: Tu decidi cosa accettare e cosa rifiutare
- Sicurezza: Il campo privato (_cilindrata) è nascosto, solo la proprietà è visibile
- 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!
// }
┌─────────────────────────────────────────────────┐
│ 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
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! ✅
| 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!
// ═══════════════════════════════════════
// 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
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}'";
❌ 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
// 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);
}
}
═══════════════════════════════════════
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?
- Apri Esplora Risorse
- Vai alla cartella del tuo progetto (es:
C:\Users\TuoNome\source\repos\ParcoAuto) - Apri la cartella
bin - Apri la cartella
Debug - 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
// ═══════════════════════════════════════
// 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);
}
}
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!
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
// ═══════════════════════════════════════════════
// 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
// ═══════════════════════════════════════════════
// 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!
// ═══════════════════════════════════════════════
// 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. 😊
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
}
| 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
- Usa sempre
using: Garantisce chiusura automatica del file - Controlla
File.Exists(): Prima di leggere, verifica che il file esista - Gestisci gli errori: Usa
try-catchper file corrotti o dati non validi - Valida i dati: Controlla lunghezza array, conversioni, campi vuoti
- Carica all’avvio: Migliora l’esperienza utente caricando automaticamente nel costruttore
- Salva spesso: Dopo ogni modifica importante (aggiungi, modifica, rimuovi)
- Salta righe problematiche: Con
continueinvece 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
// ═══════════════════════════════════════
// 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);
}
Cosa fa questa riga?
listaAuto.Sort((auto1, auto2) => auto1.Modello.CompareTo(auto2.Modello));
È più semplice di quanto sembri! Funziona così:
Sort()prende le auto due alla volta- Confronta i loro modelli con
CompareTo - Se auto1 viene prima di auto2 alfabeticamente, le lascia in quest’ordine
- Altrimenti le scambia
- 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à
// 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
// 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
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:
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).
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
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” |
// ─────────────────────────────────────────────────
// 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"
// ❌ 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
// 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
// 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”.
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
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
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.
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
Contattocon: 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
Studentecon: 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
Librocon: 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
AutoElettricaconAutonomiaKmePercentualeBatteria - Crea la classe figlia
AutoSportivaconCavalliVaporeeHasTurbo - 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
Devicecon: Marca, Modello, Prezzo (virtual per calcolo sconto) - Crea classi derivate:
Smartphonecon SistemaOperativo e MemoriaGBTabletcon HasPenna e PolliciSchermoLaptopcon 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
MetodoPagamentocon: Intestatario, Saldo - Classi derivate:
CartaCredito: Numero, Scadenza, CVV, MassimalePayPal: Email, CashbackPercentualeAppBancaria: 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
SmartDevicecon: Nome, Stanza, Stato, Consumo - Interfaccia
ITimerSupportper dispositivi con timer - Classi derivate:
LampadinaSmart: ColoreRGB, Luminosità, ITimerSupportTermostato: TemperaturaAttuale, TemperaturaTargetTapparellaElettrica: 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
Ordinecon: ID, Cliente, Indirizzo - Interfacce:
IConsegnaFrescoper catena del freddoIConsegnaUrgenteper consegne prioritarieITracciabileper tracking GPS
- Classi con interfacce multiple:
OrdineCibo: TipoCucina, Temperatura → IConsegnaFresco, IConsegnaUrgenteOrdineFarmacia: RicettaMedica → IConsegnaUrgente, ITracciabileOrdineRegalo: 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
Personaggiocon: 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)
- Liv1:
- Override a catena con
base.Attacca()per accumulare bonus - Pattern Observer: quando un personaggio muore, tutti i nemici reagiscono
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
IConsulenteper 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/protectede 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
