Programmazione a Oggetti (OOP) in C#
Fondamenti della programmazione orientata agli oggetti: classi, oggetti e incapsulamento
🎯 Introduzione alla OOP
La programmazione orientata agli oggetti (OOP) è un paradigma di programmazione
che utilizza “oggetti” per modellare dati e comportamenti. C# è un linguaggio fondamentalmente
orientato agli oggetti, progettato attorno a questi concetti.
💡 Perché la OOP è Importante?
Immagina di dover gestire un’applicazione bancaria con migliaia di conti. Senza OOP,
dovresti creare variabili separate per ogni conto (nomeCliente1,
saldoConto1, ecc.). Con OOP? Crei una classe ContoBancario
e istanzi migliaia di oggetti, ciascuno con i propri dati! 🚀
Scrivi una volta, usa ovunque
Codice strutturato e intuitivo
Modifiche isolate e sicure
Componenti indipendenti
📚 I 4 Pilastri della OOP
| Concetto | Descrizione | Esempio Reale |
|---|---|---|
| Classi e Oggetti | Le classi sono “stampi”, gli oggetti sono le “copie” create | Classe = Ricetta pizza, Oggetto = Pizza margherita |
| Incapsulamento | Nascondere dettagli interni, esporre solo interfaccia pubblica | Auto: usi volante/pedali, non tocchi il motore |
| Ereditarietà | Classi figlie acquisiscono caratteristiche della classe padre | Animale → Cane/Gatto (ereditano “muoversi”) |
| Polimorfismo | Stesso metodo, comportamenti diversi per classi diverse | Tutti gli animali “parlano”, ma in modo diverso |
Ci concentreremo sui primi due pilastri: Classi/Oggetti e
Incapsulamento. Ereditarietà e Polimorfismo saranno trattati
nella prossima lezione sui concetti avanzati.
🏗️ Definizione di una Classe
Una classe è un modello che definisce le caratteristiche (campi/proprietà)
e i comportamenti (metodi) di un tipo di oggetto.
// Definizione di una classe
public class Persona
{
// ===== CAMPI (variabili private) =====
private string nome;
private string cognome;
private int età;
// ===== COSTRUTTORE (come si crea l'oggetto) =====
public Persona(string nome, string cognome, int età)
{
this.nome = nome;
this.cognome = cognome;
this.età = età;
}
// ===== PROPRIETÀ (accesso controllato ai campi) =====
public string Nome
{
get { return nome; }
set { nome = value; }
}
public string Cognome
{
get { return cognome; }
set { cognome = value; }
}
public int Età
{
get { return età; }
set
{
if (value >= 0) // Validazione!
età = value;
}
}
// ===== METODI (cosa può fare l'oggetto) =====
public string GetNomeCompleto()
{
return $"{nome} {cognome}";
}
public bool IsMaggiorenne()
{
return età >= 18;
}
}
🔍 Componenti di una Classe
Variabili che memorizzano lo stato dell’oggetto. Di solito private.
Metodo speciale chiamato quando crei un oggetto con new.
Interfaccia pubblica per accedere ai campi. Permettono validazione.
Azioni che l’oggetto può compiere. Comportamenti.
using System;
class Program
{
static void Main()
{
// Creazione oggetto (istanza della classe)
Persona p1 = new Persona("Mario", "Rossi", 25);
// Accesso alle proprietà (lettura)
Console.WriteLine(p1.Nome); // Output: Mario
Console.WriteLine(p1.Cognome); // Output: Rossi
// Modifica proprietà (scrittura)
p1.Età = 26;
// Chiamata ai metodi
string nomeCompleto = p1.GetNomeCompleto();
Console.WriteLine(nomeCompleto); // Output: Mario Rossi
bool maggiorenne = p1.IsMaggiorenne();
Console.WriteLine($"Maggiorenne? {maggiorenne}"); // True
}
}
🔨 Costruttori
I costruttori sono metodi speciali chiamati quando crei un nuovo oggetto con
new. Hanno lo stesso nome della classe e nessun tipo di ritorno.
Se non definisci costruttori, C# ne fornisce uno vuoto automaticamente.
using System;
public class Animale
{
public string Nome;
public string Specie;
// C# fornisce automaticamente questo costruttore:
// public Animale() { }
}
class Program
{
static void Main()
{
// Utilizzo
Animale a = new Animale(); // OK - usa costruttore predefinito
a.Nome = "Fido";
a.Specie = "Cane";
Console.WriteLine($"{a.Nome} è un {a.Specie}");
}
}
Definisci costruttori con parametri per inizializzare l’oggetto.
using System;
public class Animale
{
public string Nome;
public string Specie;
// Costruttore con parametri
public Animale(string nome, string specie)
{
Nome = nome;
Specie = specie;
}
}
class Program
{
static void Main()
{
// Utilizzo
Animale a = new Animale("Fido", "Cane"); // OK
Console.WriteLine($"{a.Nome} è un {a.Specie}");
// Animale b = new Animale(); // ❌ ERRORE! Costruttore predefinito NON esiste più
}
}
Quando definisci un costruttore personalizzato, il costruttore predefinito
scompare! Se lo vuoi, devi definirlo esplicitamente.
Puoi avere più costruttori con parametri diversi.
using System;
public class Animale
{
public string Nome;
public string Specie;
public int Età;
// Costruttore 1: senza parametri
public Animale()
{
Nome = "Sconosciuto";
Specie = "Sconosciuta";
Età = 0;
}
// Costruttore 2: solo nome
public Animale(string nome)
{
Nome = nome;
Specie = "Sconosciuta";
Età = 0;
}
// Costruttore 3: nome e specie
public Animale(string nome, string specie)
{
Nome = nome;
Specie = specie;
Età = 0;
}
// Costruttore 4: tutti i parametri
public Animale(string nome, string specie, int età)
{
Nome = nome;
Specie = specie;
Età = età;
}
}
class Program
{
static void Main()
{
// Utilizzo - tutti validi!
Animale a1 = new Animale(); // Costruttore 1
Animale a2 = new Animale("Fido"); // Costruttore 2
Animale a3 = new Animale("Felix", "Gatto"); // Costruttore 3
Animale a4 = new Animale("Polly", "Pappagallo", 5); // Costruttore 4
Console.WriteLine($"{a1.Nome} - {a1.Specie}");
Console.WriteLine($"{a2.Nome} - {a2.Specie}");
Console.WriteLine($"{a3.Nome} - {a3.Specie}");
Console.WriteLine($"{a4.Nome} - {a4.Specie} - {a4.Età} anni");
}
}
Puoi chiamare un costruttore da un altro usando : this(...) per evitare duplicazione.
using System;
public class Animale
{
public string Nome;
public string Specie;
public int Età;
// Costruttore principale
public Animale(string nome, string specie, int età)
{
Nome = nome;
Specie = specie;
Età = età;
}
// Costruttore che chiama quello principale
public Animale(string nome, string specie) : this(nome, specie, 0)
{
// Chiama il costruttore sopra con età = 0
}
public Animale(string nome) : this(nome, "Sconosciuta", 0)
{
// Chiama il costruttore principale
}
}
class Program
{
static void Main()
{
Animale a1 = new Animale("Fido");
Animale a2 = new Animale("Felix", "Gatto");
Animale a3 = new Animale("Polly", "Pappagallo", 5);
Console.WriteLine($"{a1.Nome} - {a1.Specie} - {a1.Età}");
Console.WriteLine($"{a2.Nome} - {a2.Specie} - {a2.Età}");
Console.WriteLine($"{a3.Nome} - {a3.Specie} - {a3.Età}");
}
}
🔐 Proprietà (Properties)
Le proprietà forniscono un modo controllato per accedere ai campi privati.
Permettono validazione, calcolo e incapsulamento.
Definisci get e set esplicitamente per controllo totale.
using System;
public class Studente
{
// Campo privato
private string nome;
private int età;
// Proprietà completa
public string Nome
{
get
{
return nome;
}
set
{
// Validazione: non accetta stringhe vuote
if (!string.IsNullOrEmpty(value))
nome = value;
}
}
public int Età
{
get
{
return età;
}
set
{
// Validazione: solo età positive
if (value >= 0 && value <= 120)
età = value;
}
}
}
class Program
{
static void Main()
{
Studente s = new Studente();
s.Nome = "Marco";
s.Età = 20;
Console.WriteLine($"Nome: {s.Nome}, Età: {s.Età}");
// Test validazione
s.Età = 150; // Non verrà impostato (fuori range)
Console.WriteLine($"Età dopo tentativo non valido: {s.Età}"); // Ancora 20
}
}
C# crea automaticamente il campo privato nascosto.
using System;
public class Studente
{
// Proprietà auto-implementate
// C# crea automaticamente i campi privati nascosti
public string Nome { get; set; }
public string Cognome { get; set; }
public int Matricola { get; set; }
// Proprietà con valori di default (C# 6.0+)
public string Corso { get; set; } = "Non assegnato";
public bool IsIscritto { get; set; } = true;
}
class Program
{
static void Main()
{
Studente s = new Studente();
s.Nome = "Marco"; // Usa il setter
s.Cognome = "Rossi";
string nome = s.Nome; // Usa il getter
Console.WriteLine($"{s.Nome} {s.Cognome}");
Console.WriteLine($"Corso: {s.Corso}");
Console.WriteLine($"Iscritto: {s.IsIscritto}");
}
}
Set privato o solo get per proprietà immutabili.
using System;
public class Studente
{
private double[] voti;
// Solo get - impostabile solo nel costruttore
public int Matricola { get; }
// get pubblico, set privato - modificabile solo internamente
public bool IsLaureato { get; private set; }
// Proprietà calcolata (sola lettura)
public double MediaVoti
{
get
{
if (voti == null || voti.Length == 0)
return 0;
double somma = 0;
foreach (var voto in voti)
somma += voto;
return somma / voti.Length;
}
}
public Studente(int matricola)
{
Matricola = matricola; // OK solo qui
IsLaureato = false;
voti = new double[] { 28, 30, 27, 29 };
}
public void Laurea()
{
IsLaureato = true; // OK, siamo dentro la classe
}
}
class Program
{
static void Main()
{
Studente s = new Studente(12345);
Console.WriteLine($"Matricola: {s.Matricola}");
Console.WriteLine($"Laureato: {s.IsLaureato}");
Console.WriteLine($"Media voti: {s.MediaVoti}");
s.Laurea();
Console.WriteLine($"Laureato dopo laurea: {s.IsLaureato}");
// s.Matricola = 99999; // ❌ ERRORE! Solo lettura
// s.IsLaureato = true; // ❌ ERRORE! Set privato
double media = s.MediaVoti; // ✅ OK, viene calcolata
}
}
| Tipo | Quando Usarlo | Esempio |
|---|---|---|
| Auto-implementata | Dati semplici senza validazione | public string Nome { get; set; } |
| Completa | Serve validazione o logica custom | Età solo se > 0 |
| Sola lettura | Valori immutabili | Matricola, DataNascita |
| Calcolata | Derivata da altri dati | MediaVoti, NomeCompleto |
🔒 Modificatori di Accesso
I modificatori di accesso controllano chi può vedere e usare i membri
di una classe (campi, metodi, proprietà).
| Modificatore | Visibilità | Quando Usarlo |
|---|---|---|
private |
Solo dentro la classe | Campi e metodi helper interni. Default per membri. |
public |
Accessibile da ovunque | Proprietà, metodi e costruttori pubblici. Interfaccia della classe. |
protected |
Classe e classi derivate | Membri da condividere con classi figlie (ereditarietà). |
internal |
Stesso assembly | Condivisione tra classi dello stesso progetto. |
using System;
public class ContoBancario
{
// private - solo dentro questa classe
private decimal saldo;
private string numeroConto;
// public - accessibile da tutti
public string Intestatario { get; set; }
// protected - questa classe + classi derivate
protected void LogOperazione(string operazione)
{
Console.WriteLine($"[LOG] {operazione}");
}
// internal - solo dentro questo progetto
internal string CodiceBanca { get; set; }
public ContoBancario(string intestatario, string numeroConto)
{
this.Intestatario = intestatario;
this.numeroConto = numeroConto;
this.saldo = 0;
}
// Metodo pubblico per accedere al saldo privato
public decimal GetSaldo()
{
return saldo;
}
// Metodo pubblico per depositare
public void Deposita(decimal importo)
{
if (importo > 0)
{
saldo += importo;
LogOperazione($"Deposito: {importo:C}");
}
}
}
class Program
{
static void Main()
{
ContoBancario conto = new ContoBancario("Mario Rossi", "IT123456");
conto.Intestatario = "Mario Rossi"; // ✅ OK - public
conto.Deposita(100); // ✅ OK - public
decimal saldo = conto.GetSaldo(); // ✅ OK - public
Console.WriteLine($"Intestatario: {conto.Intestatario}");
Console.WriteLine($"Saldo: €{saldo}");
// conto.saldo = 1000; // ❌ ERRORE - private
// conto.numeroConto = "..."; // ❌ ERRORE - private
// conto.LogOperazione("test"); // ❌ ERRORE - protected
}
}
- Rendi i campi sempre private
- Esponi l'accesso tramite proprietà pubbliche
- Usa metodi pubblici per operazioni
- Mantieni private le implementazioni interne
- Principio: "Nascondere i dettagli, esporre l'interfaccia"
🎨 Creazione e Utilizzo di Oggetti
Una volta definita la classe, puoi creare istanze (oggetti) e utilizzarle.
using System;
public class Prodotto
{
// Proprietà auto-implementate
public string Nome { get; set; }
public decimal Prezzo { get; set; }
public int Quantità { get; set; }
// Costruttore
public Prodotto(string nome, decimal prezzo, int quantità)
{
Nome = nome;
Prezzo = prezzo;
Quantità = quantità;
}
// Metodo per calcolare valore totale
public decimal CalcolaValore()
{
return Prezzo * Quantità;
}
// Metodo per applicare sconto
public void ApplicaSconto(decimal percentuale)
{
if (percentuale > 0 && percentuale <= 100)
{
decimal sconto = Prezzo * (percentuale / 100);
Prezzo -= sconto;
}
}
// Override ToString per visualizzazione
public override string ToString()
{
return $"{Nome} - €{Prezzo:F2} x {Quantità} = €{CalcolaValore():F2}";
}
}
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// ===== CREAZIONE OGGETTI =====
Prodotto p1 = new Prodotto("Laptop", 999.99m, 5);
Prodotto p2 = new Prodotto("Mouse", 19.99m, 10);
Prodotto p3 = new Prodotto("Tastiera", 49.99m, 8);
// ===== ACCESSO PROPRIETÀ =====
Console.WriteLine($"Nome: {p1.Nome}");
Console.WriteLine($"Prezzo: €{p1.Prezzo}");
Console.WriteLine($"Quantità: {p1.Quantità}");
// ===== MODIFICA PROPRIETÀ =====
p1.Quantità = 7;
p2.Prezzo = 17.99m;
// ===== CHIAMATA METODI =====
decimal valore1 = p1.CalcolaValore();
decimal valore2 = p2.CalcolaValore();
decimal valore3 = p3.CalcolaValore();
Console.WriteLine($"\nValore prodotto 1: €{valore1}");
Console.WriteLine($"Valore prodotto 2: €{valore2}");
Console.WriteLine($"Valore prodotto 3: €{valore3}");
decimal valoreTotale = valore1 + valore2 + valore3;
Console.WriteLine($"\n💰 Valore totale magazzino: €{valoreTotale:F2}");
// ===== APPLICAZIONE SCONTO =====
Console.WriteLine("\n--- Applicazione sconto 20% al laptop ---");
p1.ApplicaSconto(20);
Console.WriteLine(p1.ToString());
// ===== LAVORARE CON COLLEZIONI DI OGGETTI =====
List magazzino = new List { p1, p2, p3 };
Console.WriteLine("\n📦 INVENTARIO:");
foreach (Prodotto prodotto in magazzino)
{
Console.WriteLine(prodotto.ToString());
}
}
}
🚀 Esempio Completo: Sistema Gestione Biblioteca
Vediamo un sistema completo che utilizza classi, oggetti, incapsulamento e collezioni.
using System;
public class Libro
{
// ===== PROPRIETÀ =====
public string Titolo { get; set; }
public string Autore { get; set; }
public string ISBN { get; set; }
public int AnnoPubblicazione { get; set; }
// Disponibile ha set privato - modificabile solo internamente
public bool Disponibile { get; private set; }
// ===== COSTRUTTORE =====
public Libro(string titolo, string autore, string isbn, int annoPubblicazione)
{
Titolo = titolo;
Autore = autore;
ISBN = isbn;
AnnoPubblicazione = annoPubblicazione;
Disponibile = true; // Inizialmente disponibile
}
// ===== METODI PUBBLICI =====
public void Presta()
{
if (Disponibile)
{
Disponibile = false;
Console.WriteLine($"✅ Libro '{Titolo}' prestato con successo.");
}
else
{
Console.WriteLine($"❌ Libro '{Titolo}' non disponibile per il prestito.");
}
}
public void Restituisci()
{
if (!Disponibile)
{
Disponibile = true;
Console.WriteLine($"✅ Libro '{Titolo}' restituito con successo.");
}
else
{
Console.WriteLine($"⚠️ Errore: Libro '{Titolo}' era già disponibile.");
}
}
public string GetInfo()
{
string stato = Disponibile ? "📗 Disponibile" : "📕 In prestito";
return $"{Titolo} - {Autore} ({AnnoPubblicazione}) - {stato}";
}
public override string ToString()
{
return GetInfo();
}
}
using System;
using System.Collections.Generic;
public class Biblioteca
{
// Campo privato - incapsulamento!
private List catalogo;
// Proprietà calcolata
public int NumeroLibri
{
get { return catalogo.Count; }
}
// ===== COSTRUTTORE =====
public Biblioteca()
{
catalogo = new List();
}
// ===== METODI PUBBLICI =====
public void AggiungiLibro(Libro libro)
{
catalogo.Add(libro);
Console.WriteLine($"📚 Libro '{libro.Titolo}' aggiunto al catalogo.");
}
public void RimuoviLibro(string isbn)
{
Libro libro = TrovaLibro(isbn);
if (libro != null)
{
catalogo.Remove(libro);
Console.WriteLine($"🗑️ Libro '{libro.Titolo}' rimosso dal catalogo.");
}
else
{
Console.WriteLine($"❌ Libro con ISBN {isbn} non trovato.");
}
}
public Libro TrovaLibro(string isbn)
{
foreach (var libro in catalogo)
{
if (libro.ISBN == isbn)
{
return libro;
}
}
return null;
}
public void PrestaLibro(string isbn)
{
Libro libro = TrovaLibro(isbn);
if (libro != null)
{
libro.Presta();
}
else
{
Console.WriteLine($"❌ Libro con ISBN {isbn} non trovato.");
}
}
public void RestituisciLibro(string isbn)
{
Libro libro = TrovaLibro(isbn);
if (libro != null)
{
libro.Restituisci();
}
else
{
Console.WriteLine($"❌ Libro con ISBN {isbn} non trovato.");
}
}
public void MostraCatalogo()
{
if (catalogo.Count == 0)
{
Console.WriteLine("📭 Il catalogo è vuoto.");
return;
}
Console.WriteLine("\n" + new string('=', 60));
Console.WriteLine($"📖 CATALOGO BIBLIOTECA ({NumeroLibri} libri)");
Console.WriteLine(new string('=', 60));
foreach (var libro in catalogo)
{
Console.WriteLine(libro.GetInfo());
}
Console.WriteLine(new string('=', 60));
}
public void MostraStatistiche()
{
int disponibili = 0;
int prestati = 0;
foreach (var libro in catalogo)
{
if (libro.Disponibile)
disponibili++;
else
prestati++;
}
Console.WriteLine("\n📊 STATISTICHE BIBLIOTECA");
Console.WriteLine($"Totale libri: {NumeroLibri}");
Console.WriteLine($"Disponibili: {disponibili}");
Console.WriteLine($"In prestito: {prestati}");
}
}
using System;
class Program
{
static void Main()
{
Console.WriteLine("=== SISTEMA GESTIONE BIBLIOTECA ===\n");
// Creazione biblioteca
Biblioteca biblioteca = new Biblioteca();
// Aggiunta libri
Console.WriteLine("--- Aggiunta Libri ---");
biblioteca.AggiungiLibro(new Libro(
"Il Signore degli Anelli",
"J.R.R. Tolkien",
"9788830101531",
1954
));
biblioteca.AggiungiLibro(new Libro(
"1984",
"George Orwell",
"9788804668014",
1949
));
biblioteca.AggiungiLibro(new Libro(
"Il Nome della Rosa",
"Umberto Eco",
"9788845292866",
1980
));
biblioteca.AggiungiLibro(new Libro(
"Harry Potter e la Pietra Filosofale",
"J.K. Rowling",
"9788831003384",
1997
));
// Visualizzazione catalogo
biblioteca.MostraCatalogo();
// Test funzionalità
Console.WriteLine("\n--- Test Prestiti ---");
biblioteca.PrestaLibro("9788830101531"); // Signore degli Anelli
biblioteca.PrestaLibro("9788804668014"); // 1984
biblioteca.PrestaLibro("9788830101531"); // Prova a prestare di nuovo
// Catalogo aggiornato
biblioteca.MostraCatalogo();
// Statistiche
biblioteca.MostraStatistiche();
// Restituzione
Console.WriteLine("\n--- Test Restituzione ---");
biblioteca.RestituisciLibro("9788830101531");
// Rimozione
Console.WriteLine("\n--- Test Rimozione ---");
biblioteca.RimuoviLibro("9788804668014");
// Stato finale
biblioteca.MostraCatalogo();
biblioteca.MostraStatistiche();
}
}
- Classe Libro: Rappresenta un singolo libro con proprietà e comportamenti
- Incapsulamento:
Disponibileha set privato, modificabile solo daPresta()/Restituisci() - Classe Biblioteca: Gestisce una collezione di libri (List privata)
- Separazione responsabilità: Libro sa gestirsi, Biblioteca gestisce il catalogo
- Metodi pubblici: Interfaccia chiara (Aggiungi, Rimuovi, Presta, ecc.)
- Validazione: Controlli prima di operazioni (libro null, già prestato, ecc.)
- Classi come modelli
- Oggetti come istanze
- Costruttori per inizializzazione
- Campi privati per dati
- Proprietà per incapsulamento
- Modificatori di accesso
- Metodi pubblici/privati
- Separazione responsabilità
Lezione 13 del Corso C# Completo