COS'È UNA CLASSE ASTRATTA IN C#?
Una classe astratta in C# è una classe che non può essere istanziata direttamente, ma può essere
utilizzata come base per altre classi. Le classi astratte vengono generalmente utilizzate per definire
una struttura comune che le classi derivate devono seguire, ma lasciano la possibilità di
implementare dettagli specifici nelle classi figlie.
In altre parole, una classe astratta può contenere sia metodi astratti, che non hanno un corpo e
devono essere implementati dalle classi derivate, sia metodi concreti, che hanno una loro
implementazione e possono essere utilizzati o sovrascritti nelle classi figlie.
Caratteristiche di una classe astratta in C#:
1. Non può essere istanziata direttamente: Non è possibile creare un oggetto di tipo di una
classe astratta.
2. Può avere metodi astratti: Questi metodi sono definiti senza implementazione nella classe
astratta e devono essere implementati nelle classi derivate.
3. Può avere metodi concreti: Questi metodi sono implementati nella classe astratta e
possono essere usati direttamente dalle classi derivate, oppure possono essere sovrascritti.
4. Può avere proprietà, campi e costruttori: Come le classi normali, anche le classi astratte
possono avere variabili di istanza, proprietà, e costruttori.
La parola chiave abstract viene utilizzata per dichiarare una classe astratta e per dichiarare
metodi astratti.
public abstract class Animale Classe astratta Animale:
{
// Campo
public string Nome { get; set; } - Contiene una proprietà Nome.
// Metodo concreto - Ha un metodo concreto Dormi() che può
public void Dormi() essere utilizzato dalle classi derivate.
{
[Link](Nome + " sta
dormendo.");
}
- Ha un metodo astratto FaiVerso() che non ha
// Metodo astratto (senza corpo) implementazione e deve essere
public abstract void FaiVerso(); obbligatoriamente implementato nelle classi
} derivate.
public class Cane : Animale public class Gatto : Animale
{ {
public Cane(string nome) public Gatto(string nome)
{ {
Nome = nome; Nome = nome;
} }
// Implementazione del metodo astratto // Implementazione del metodo astratto
public override void FaiVerso() public override void FaiVerso()
{ {
[Link](Nome + " fa: Bau!"); [Link](Nome + " fa: Miao!");
} }
} }
Classi derivate Cane e Gatto:
- Entrambe le classi derivano dalla classe astratta Animale.
- Ognuna implementa il metodo astratto FaiVerso(), che produce un comportamento diverso a
seconda del tipo di animale.
// codice consumer
class Program
{
static void Main(string[] args)
{
// Non è possibile creare un oggetto della classe astratta
// Animale animale = new Animale(); // Errore: 'Animale' è una classe astratta
// Creazione di oggetti derivati dalle classi concrete
Cane cane = new Cane("Fido");
Gatto gatto = new Gatto("Micio");
// Chiamate ai metodi
[Link]();
[Link]();
[Link]();
[Link]();
}
}
Nel metodo Main:
- Non è possibile creare un oggetto della classe astratta Animale direttamente (commentato con
un errore nel codice).
- Vengono creati oggetti di tipo Cane e Gatto, che sono classi concrete derivate dalla classe
astratta.
Vantaggi dell'uso delle classi astratte:
- Struttura comune: Le classi astratte forniscono una struttura comune per le classi derivate,
definendo un insieme di metodi e proprietà che devono essere implementati o ereditati.
- Ereditarietà parziale: Le classi astratte permettono di implementare solo una parte della
funzionalità, lasciando alle classi derivate la possibilità di implementare i dettagli specifici.
- Polimorfismo: Le classi astratte favoriscono il polimorfismo, poiché le classi derivate possono
essere trattate come oggetti della classe base.
Quando usare una classe astratta?
- Quando si desidera fornire una base comune per le classi derivate, ma non si vuole che gli utenti
della classe base possano creare istanze di essa.
- Quando alcune funzionalità sono comuni a tutte le classi derivate (come metodi concreti), ma
altre devono essere implementate in modo specifico nelle classi derivate (come metodi astratti).
Differenza tra classe astratta e interfaccia:
- Classe astratta: Può avere sia metodi astratti (senza implementazione) che metodi concreti (con
implementazione). Può anche avere campi e proprietà.
- Interfaccia: Contiene solo dichiarazioni di metodi, proprietà, eventi senza implementazione. Le
classi che implementano un'interfaccia devono implementare tutti i membri dell'interfaccia.
Le classi astratte sono quindi più adatte quando si vuole fornire una base con
implementazioni parziali, mentre le interfacce sono più adatte per definire un
contratto che deve essere seguito dalle classi che le implementano.
CONTRATTO in una INTERFACCIA
In C#, un contratto in una interfaccia si riferisce a un insieme di regole che una
classe deve seguire quando implementa quell'interfaccia. In altre parole,
un'interfaccia definisce un set di metodi e proprietà che una classe deve
implementare. Questi metodi e proprietà costituiscono il contratto che la classe deve
rispettare.
Cos'è un contratto in un'interfaccia?
Un contratto stabilisce cosa una classe deve fare, ma non come lo deve fare. In
altre parole, l'interfaccia impone un comportamento senza fornire dettagli su come
questi comportamenti siano realizzati. La classe che implementa l'interfaccia è
responsabile di fornire le implementazioni concrete.
Sintassi di un'interfaccia in C#
Un'interfaccia in C# viene dichiarata con la parola chiave interface. L'interfaccia può
contenere la dichiarazione di metodi, proprietà, eventi ma senza implementazione.
Le classi che implementano l'interfaccia devono fornire l'implementazione dei
metodi definiti nell'interfaccia.
Esempio di interfaccia in C# (contratto):
using System;
// Definizione dell'interfaccia
public interface IDocumento
{
// Proprietà del contratto
string Titolo { get; set; }
// Metodo del contratto (da implementare nelle classi)
void Stampa();
}
// Classe che implementa l'interfaccia
public class DocumentoPdf : IDocumento
{
public string Titolo { get; set; }
// Implementazione del metodo Stampa
public void Stampa()
{
[Link]("Stampa del documento PDF: Titolo");
// Classe che implementa l'interfaccia
public class DocumentoWord : IDocumento
public string Titolo get; set;
// Implementazione del metodo Stampa
public void Stampa()
[Link]("Stampa del documento Word: {Titolo}");
}
}
class Program
{
static void Main()
{
// Creazione di oggetti che implementano l'interfaccia
IDocumento documentoPdf = new DocumentoPdf { Titolo = "Manuale PDF" };
IDocumento documentoWord = new DocumentoWord { Titolo = "Manuale Word" };
// Chiamata al metodo di stampa
[Link]();
[Link]();
}
}
```
Dettagli:
1. Interfaccia IDocumento: Definisce un contratto con una proprietà Titolo e un
metodo Stampa(). La classe che implementa questa interfaccia deve fornire una
propria implementazione del metodo Stampa() e una gestione della proprietà Titolo.
2. Classi DocumentoPdf e DocumentoWord: Entrambe implementano l'interfaccia
IDocumento. Ogni classe fornisce la propria implementazione del metodo Stampa(),
ma entrambe sono obbligate a gestire la proprietà Titolo.
3. Polimorfismo: Nel Main, creiamo due oggetti di tipo IDocumento, uno per il PDF e
uno per il Word. Sebbene entrambi gli oggetti siano trattati come IDocumento,
ciascuno ha una propria implementazione del metodo Stampa().
Perché usare le interfacce (contratti)?
Le interfacce sono utilizzate principalmente per:
1. Definire un contratto: L'interfaccia stabilisce un insieme di metodi e proprietà che
una classe deve implementare. Questo permette di definire un comportamento
comune che può essere utilizzato da classi diverse, anche se queste ultime hanno
implementazioni differenti.
2. Garantire il polimorfismo: Le interfacce permettono di trattare oggetti di classi
diverse in modo uniforme. Ad esempio, tutte le classi che implementano
IDocumento possono essere trattate come un unico tipo IDocumento,
indipendentemente dalla loro implementazione concreta.
3. Facilitare l'astrazione: Le interfacce permettono di astrarre il comportamento
senza legarsi a implementazioni specifiche. Un'applicazione può interagire con
oggetti attraverso l'interfaccia, senza preoccuparsi dei dettagli di implementazione.
4. Supportare l'ereditarietà multipla: Una classe può implementare più di
un'interfaccia, il che consente di ereditare comportamenti da più sorgenti. Questo
approccio è utile per i casi in cui una classe deve svolgere più ruoli, ognuno definito
da una diversa interfaccia.
public interface IStampa {
void Stampa();
}
public interface IArchivia {
void Archivia();
}
public class Documento : IStampa, IArchivia
{
public void Stampa() {
[Link]("Documento stampato");
}
public void Archivia() {
[Link]("Documento archiviato");
}
}
Un contratto in un'interfaccia è una promessa formale di ciò che una classe deve fare, ma non
specifica come farlo. Le interfacce sono strumenti potenti in C# per ottenere polimorfismo,
astrazione, e flessibilità, rendendo il codice più modulare, testabile e facilmente manutenibile.