overrideclass Auto
{
private string _marca;
private int _vMax;
public Auto(string marca, int vMax) { ... } // costruttore
public void Avvia() { Console.WriteLine("Avvio motore"); }
public void Ferma() { Console.WriteLine("Freno"); }
}
class Moto
{
private string _marca; // duplicato!
private int _vMax; // duplicato!
public Moto(string marca, int vMax) { ... }
public void Avvia() { Console.WriteLine("Avvio motore"); } // duplicato!
public void Ferma() { Console.WriteLine("Freno"); } // duplicato!
}Moto è un Veicolo// Classe BASE
class Veicolo
{
// campi e metodi comuni a tutti i veicoli
}
// Classe DERIVATA: il simbolo ':' indica "estende"
class Auto : Veicolo
{
// solo i dati e metodi SPECIFICI di Auto
}
class Moto : Veicolo
{
// solo i dati e metodi SPECIFICI di Moto
}In C# ogni classe può ereditare da una sola classe base (ereditarietà singola).
class Veicolo
{
private string _marca;
private int _velocitaMax;
public string Marca { get { return _marca; } }
public int VelocitaMax { get { return _velocitaMax; } }
public Veicolo(string marca, int velocitaMax)
{
_marca = marca;
_velocitaMax = velocitaMax > 0 ? velocitaMax : 0;
}
public void Avvia()
{
Console.WriteLine($"{_marca}: motore avviato.");
}
public void Ferma()
{
Console.WriteLine($"{_marca}: veicolo fermo.");
}
public override string ToString()
{
return $"{_marca} (max {_velocitaMax} km/h)";
}
}
class Auto : Veicolo
{
private int _numeroPosti;
public int NumeroPosti { get { return _numeroPosti; } }
// Il costruttore della derivata chiama quello della base con 'base(...)'
public Auto(string marca, int velocitaMax, int posti)
: base(marca, velocitaMax)
{
_numeroPosti = posti > 0 ? posti : 2;
}
public void ApriCofano()
{
Console.WriteLine("Cofano aperto.");
}
public override string ToString()
{
return base.ToString() + $", {_numeroPosti} posti";
}
}basebase(...) nel costruttore: chiama il
costruttore della classe basebase.Metodo(): chiama la versione del
metodo definita nella classe basebase(...), C# chiama
automaticamente il costruttore senza parametri della
base (errore se non esiste)Veicolo v = new Veicolo("Generico", 200);
Auto a = new Auto("Fiat", 180, 5);
v.Avvia(); // Generico: motore avviato.
a.Avvia(); // Fiat: motore avviato. ← ereditato da Veicolo
a.ApriCofano(); // Cofano aperto. ← specifico di Auto
Console.WriteLine(v); // Generico (max 200 km/h)
Console.WriteLine(a); // Fiat (max 180 km/h), 5 postiAuto ha tutti i metodi
di Veicolo più i propri.
protected: visibile alle classi
derivate| Modificatore | Stessa classe | Classe derivata | Fuori dalla gerarchia |
|---|---|---|---|
public |
sì | sì | sì |
protected |
sì | sì | no |
private |
sì | no | no |
Usare protected per campi o metodi che
devono essere accessibili alle classi derivate ma non all'esterno.
protectedclass Veicolo
{
protected string _marca; // accessibile nelle classi derivate
private int _codiceInterno; // non accessibile nelle derivate
public Veicolo(string marca)
{
_marca = marca;
}
}
class Auto : Veicolo
{
public Auto(string marca) : base(marca) { }
public void Descrivi()
{
Console.WriteLine(_marca); // OK: è protected
// Console.WriteLine(_codiceInterno); // ERRORE: è private
}
}virtual e override
class Veicolo
{
public string Marca { get; }
public Veicolo(string marca) { Marca = marca; }
// virtual: le classi derivate POSSONO ridefinire questo metodo
public virtual string Descrizione()
{
return $"Veicolo: {Marca}";
}
}
class Auto : Veicolo
{
public int NumeroPosti { get; }
public Auto(string marca, int posti) : base(marca)
{
NumeroPosti = posti;
}
// override: ridefinisce il metodo virtuale della base
public override string Descrizione()
{
return $"Auto: {Marca}, {NumeroPosti} posti";
}
}override con chiamata a baseclass Camion : Veicolo
{
public decimal PortataKg { get; }
public Camion(string marca, decimal portata) : base(marca)
{
PortataKg = portata;
}
public override string Descrizione()
{
// Riusa la descrizione della base e aggiunge informazioni
return base.Descrizione() + $" [portata: {PortataKg} kg]";
}
}base.Descrizione() chiama la versione
di Veicolo, evitando di duplicare la
logica.
// Un array di Veicolo può contenere qualsiasi tipo derivato
Veicolo[] parco = {
new Veicolo("Bici", 30),
new Auto("Toyota", 160, 5),
new Camion("Scania", 18000)
};
// Lo stesso codice funziona per tutti i tipi
foreach (Veicolo v in parco)
{
Console.WriteLine(v.Descrizione());
}Veicolo: Bici
Auto: Toyota, 5 posti
Veicolo: Scania [portata: 18000 kg]
Veicolo v = new Auto("Fiat", 5); è
sempre lecitoAuto a = new Veicolo(...); è un erroreoverride, l'overloading non
richiede ereditarietàSommaInt, SommaDouble, …)Due metodi si considerano diversi se cambia almeno una di queste caratteristiche:
Non è sufficiente, invece:
Calcolatriceclass Calcolatrice
{
// 1) Somma di due interi
public int Somma(int a, int b)
{
return a + b;
}
// 2) Somma di tre interi (diverso NUMERO di parametri)
public int Somma(int a, int b, int c)
{
return a + b + c;
}
// 3) Somma di due double (diverso TIPO di parametri)
public double Somma(double a, double b)
{
return a + b;
}
// 4) Somma di tutti gli elementi di un array
public int Somma(int[] valori)
{
int totale = 0;
foreach (int v in valori) totale += v;
return totale;
}
}Calcolatrice c = new Calcolatrice();
Console.WriteLine(c.Somma(2, 3)); // chiama la 1) → 5
Console.WriteLine(c.Somma(2, 3, 4)); // chiama la 2) → 9
Console.WriteLine(c.Somma(1.5, 2.5)); // chiama la 3) → 4
Console.WriteLine(c.Somma(new[] {1, 2, 3, 4})); // chiama la 4) → 10Il compilatore individua la versione corretta osservando tipi e numero degli argomenti.
Anche i costruttori possono essere sovraccaricati: è utile per fornire versioni con meno parametri e valori di default.
class Veicolo
{
public string Marca { get; }
public int VelocitaMax { get; }
// Costruttore completo
public Veicolo(string marca, int velocitaMax)
{
Marca = marca;
VelocitaMax = velocitaMax;
}
// Costruttore ridotto: delega al completo con 'this(...)'
public Veicolo(string marca) : this(marca, 130) { }
// Costruttore senza parametri
public Veicolo() : this("Generico", 0) { }
}this(...) evita di duplicare la logica
di inizializzazione.
class Stampante
{
public void Stampa(string testo) { Console.WriteLine(testo); }
}
class StampanteAvanzata : Stampante
{
// nuovo overload: stessa "famiglia" di metodi, firma diversa
public void Stampa(string testo, int copie)
{
for (int i = 0; i < copie; i++) Console.WriteLine(testo);
}
}
StampanteAvanzata s = new StampanteAvanzata();
s.Stampa("ciao"); // ereditato dalla base
s.Stampa("ciao", 3); // nuovo overload| Aspetto | Overloading | Overriding |
|---|---|---|
| Firma | diversa | uguale |
| Richiede eredità | no | sì |
| Parole chiave | nessuna | virtual / override |
| Quando viene scelto | a compile time | a run time |
| Scopo | stessa operazione, dati diversi | specializzare comportamento |
Talvolta la classe base rappresenta un concetto astratto che non ha senso istanziare direttamente:
Meglio impedire la creazione diretta e obbligare le classi derivate a completare l'implementazione.
// 'abstract': non si può fare new Animale()
abstract class Animale
{
public string Nome { get; }
public Animale(string nome) { Nome = nome; }
// 'abstract': nessuna implementazione qui,
// le classi derivate DEVONO fare override
public abstract string FaiVerso();
// metodo normale: ereditato da tutti
public void Presentati()
{
Console.WriteLine($"Sono {Nome} e dico: {FaiVerso()}");
}
}class Cane : Animale
{
public Cane(string nome) : base(nome) { }
public override string FaiVerso() // OBBLIGATORIO
{
return "Bau!";
}
}
class Gatto : Animale
{
public Gatto(string nome) : base(nome) { }
public override string FaiVerso() // OBBLIGATORIO
{
return "Miao!";
}
}Se una classe derivata non implementa tutti i metodi astratti, deve essere astratta a sua volta.
// Animale a = new Animale("x"); // ERRORE: classe astratta
Animale[] zoo = {
new Cane("Rex"),
new Gatto("Whiskers"),
new Cane("Fido")
};
foreach (Animale a in zoo)
{
a.Presentati();
}Sono Rex e dico: Bau!
Sono Whiskers e dico: Miao!
Sono Fido e dico: Bau!
abstract class Figura
{
public string Colore { get; set; }
public Figura(string colore)
{
Colore = colore;
}
public abstract double Area();
public abstract double Perimetro();
public override string ToString()
{
return $"{GetType().Name} ({Colore}): " +
$"area={Area():F2}, perimetro={Perimetro():F2}";
}
}class Cerchio : Figura
{
public double Raggio { get; }
public Cerchio(double raggio, string colore) : base(colore)
{
Raggio = raggio > 0 ? raggio : 0;
}
public override double Area() => Math.PI * Raggio * Raggio;
public override double Perimetro() => 2 * Math.PI * Raggio;
}
class Rettangolo : Figura
{
public double Base { get; }
public double Altezza { get; }
public Rettangolo(double b, double h, string colore) : base(colore)
{
Base = b > 0 ? b : 0;
Altezza = h > 0 ? h : 0;
}
public override double Area() => Base * Altezza;
public override double Perimetro() => 2 * (Base + Altezza);
}List<Figura> figure = new List<Figura>
{
new Cerchio(5.0, "rosso"),
new Rettangolo(4.0, 6.0, "blu"),
new Cerchio(3.0, "verde")
};
foreach (Figura f in figure)
{
Console.WriteLine(f);
}
// Area totale — funziona per qualsiasi Figura
double totale = 0;
foreach (Figura f in figure)
totale += f.Area();
Console.WriteLine($"Area totale: {totale:F2}");Cerchio (rosso): area=78.54, perimetro=31.42
Rettangolo (blu): area=24.00, perimetro=20.00
Cerchio (verde): area=28.27, perimetro=18.85
Area totale: 130.81
| Concetto | Sintassi C# |
|---|---|
| Classe derivata | class B : A { ... } |
| Costruttore derivata | public B(...) : base(...) { ... } |
| Membro protetto | protected tipo _campo; |
| Concetto | Sintassi C# |
|---|---|
| Metodo ridefinibile | public virtual T Metodo() { ... } |
| Ridefinizione | public override T Metodo() { ... } |
| Chiamata alla base | base.Metodo() |
| Classe astratta | abstract class A { ... } |
| Metodo astratto | public abstract T Metodo(); |
| Overload di metodo | stesso nome, firma diversa |
| Concatena costruttori | public B(...) : this(...) { ... } |
Uno sguardo critico all'OOP: https://cscalfani.medium.com/goodbye-object-oriented-programming-a59cda4c0e53