Ereditarietà (Guida per programmatori C#)
Aggiornamento: Luglio 2008
L’ereditarietà, insieme con l’incapsulamento e il polimorfismo, rappresenta una delle tre principali caratteristiche (o "pilastri") della programmazione orientata a oggetti. L'ereditarietà permette di creare nuove classi che riutilizzano, estendono e modificano il comportamento definito in altre classi. La classe i cui membri vengono ereditati è denominata classe base, mentre la classe che eredita tali membri è denominata classe derivata.
Nota: |
---|
Le strutture non supportano l'ereditarietà, mentre possono implementare interfacce. Per ulteriori informazioni, vedere la classe Interfacce (Guida per programmatori C#). |
Concettualmente, una classe derivata rappresenta una specializzazione della classe base. Ad esempio, avendo una classe base Animal, è possibile definire una classe derivata denominata Mammal e un’altra classe derivata denominata Reptile. Un oggetto Mammal è anche un oggettoAnimal e un oggetto Reptile è anche un Animal, ma ciascuna classe derivata rappresenta una diversa specializzazione della classe base.
Quando si definisce una classe derivandola da un'altra classe, la classe derivata acquista implicitamente tutti i membri della classe base, con l’eccezione dei costruttori e dei distruttori. Di conseguenza, la classe derivata può riutilizzare il codice definito nella classe base senza doverlo implementare nuovamente. Nella classe derivata è possibile aggiungere altri membri . In questo modo, la classe derivata estende la funzionalità della classe base.
Nella figura riportata di seguito viene mostrata una classe WorkItem che rappresenta un elemento di lavoro in un qualche processo aziendale. Come per tutte le classi, è derivata da System.Object ed eredita tutti i metodi di tale classe. La classe WorkItem aggiunge cinque membri propri. Fra questi è incluso un costruttore, in quanto i costruttori non vengono ereditati. La classe ChangeRequest eredita dalla classe WorkItem e rappresenta un particolare tipo di elemento di lavoro. La classe ChangeRequest aggiunge due ulteriori membri all’elenco di membri ereditati da WorkItem e da Object. La classe deve aggiungere un proprio costruttore e aggiunge inoltre un membro che consentirà l’associazione tra l’oggetto ChangeRequest e l’oggetto WorkItem originale al quale si applica la modifica.
Ereditarietà delle classi
Nell'esempio seguente viene illustrato come le relazioni tra le classi mostrate nella precedente illustrazione vengono espresse in C#. Nell'esempio viene inoltre descritto come la classe WorkItem implementa l’override del metodo virtuale Object.ToString e come la classe ChangeRequest eredita l’implementazione del metodo definito dalla classe WorkItem.
// WorkItem implicitly inherits from Object class
public class WorkItem
{
private static int nextID;
protected int ID { get; set; }
protected TimeSpan jobLength { get; set; }
protected string Title { get; set; }
protected string Description { get; set; }
// Default constructor
public WorkItem()
{
ID = 0;
Title = "Default title";
Description = "Default description.";
jobLength = new TimeSpan();
}
// Static constructor for static member.
static WorkItem()
{
nextID = 0;
}
// Instance constructor.
public WorkItem( string title, string desc, TimeSpan joblen)
{
this.ID = GetNextID();
this.Title = title;
this.Description = desc;
this.jobLength = joblen;
}
protected int GetNextID()
{
return ++nextID;
}
public void Update(string title, TimeSpan joblen)
{
this.Title = title;
this.jobLength = joblen;
}
// Virtual method override.
public override string ToString()
{
return String.Format("{0} - {1}", this.ID, this.Title);
}
}
// ChangeRequest derives from WorkItem and adds two of its own members.
public class ChangeRequest : WorkItem
{
protected int originalItemID {get; set;}
public ChangeRequest() { }
public ChangeRequest(string title, string desc, TimeSpan jobLen, int originalID)
{
this.ID = GetNextID();
this.Title = title;
this.Description = desc;
this.jobLength = jobLen;
this.originalItemID = originalID;
}
}
class Program
{
static void Main()
{
WorkItem item = new WorkItem(
"Fix Bugs",
"Fix all bugs in my source code branch",
new TimeSpan(3, 4, 0, 0));
ChangeRequest change = new ChangeRequest("Change design of base class",
"Add members to base class",
new TimeSpan(4, 0, 0),
1);
Console.WriteLine(item.ToString());
// ChangeRequest inherits WorkItem's override of ToString
Console.WriteLine(change.ToString());
// Keep the console open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
1 - Fix Bugs
2 - Change design of base class
*/
Metodi virtuali e astratti
Quando una classe base dichiara un metodo come virtuale, una classe derivata può eseguire l'override del metodo definendo una propria implementazione. Quando una classe di base dichiara un membro come astratto, è necessario effettuare l'override di tale metodo in ogni classe non astratta che eredita direttamente da tale classe. Quando una classe derivata è dichiarata a sua volta astratta, eredita i membri astratti senza implementarli. I membri astratti e virtuali costituiscono la base del polimorfismo, che rappresenta la seconda principale caratteristica della programmazione orientata a oggetti. Per ulteriori informazioni, vedere Polimorfismo (Guida per programmatori C#).
Classi base astratte
Quando si desidera prevenire la generazione di istanze dirette di una classe, è possibile dichiararla come astratta utilizzando la parola chiave new. In questo modo, la classe è utilizzabile soltanto quando una nuova classe viene derivata da essa. Una classe astratta può contenere una o più firme di metodi a loro volta dichiarati come astratti. Tali firme specificano i parametri e il valore restituito, ma non definiscono alcuna implementazione (corpo del metodo). Una classe astratta non deve necessariamente contenere membri astratti. Tuttavia, quando una classe contiene un membro astratto, deve essere dichiarata come astratta. Le classi derivate non definite come astratte devono fornire l'implementazione per qualsiasi metodo astratto ereditato da una classe base astratta. Per ulteriori informazioni, vedere°Classi e membri delle classi astratte e sealed (Guida per programmatori C#) e Progettazione di classi astratte.
Interfacce
Un'interfaccia rappresenta un tipo di riferimento ed è per vari aspetti simile a una classe base astratta costituita solo da membri astratti. Quando una classe deriva da un'interfaccia, deve fornire un'implementazione per tutti i membri definiti nell’interfaccia. Una classe può implementare più interfacce, anche se può essere derivata solo da una singola classe base diretta.
Le interfacce sono utilizzate per definire specifiche funzionalità per le classi che non sono necessariamente caratterizzate da una relazione di tipo "è un". Ad esempio, l’interfaccia IEquatable[`1] può essere implementata da qualunque classe o struttura che deve attivare il codice client per determinare se due oggetti di un dato tipo sono equivalenti (comunque il tipo definisce l’equivalenza). IEquatable<T> non implica lo stesso genere di relazione di tipo “è una” esistente tra una classe base e una classe derivata (ad esempio, un Mammal è un Animal). Per ulteriori informazioni, vedere la classe Interfacce (Guida per programmatori C#).
Accesso ai membri di una classe base da parte di una classe derivata
Una classe derivata ha accesso ai membri di una classe di base dichiarati come public, protected, internal e protected internal. Sebbene una classe derivata erediti i membri privati di una classe base, non può avere accesso a tali membri. Tuttavia, tutti i membri privati continuano a esistere nella classe derivata e possono operare esattamente come farebbero nella classe base stessa. Ad esempio, si supponga che un metodo protetto della classe base acceda a un campo privato. Quel campo deve essere presente nella classe derivata per permettere il corretto funzionamento del metodo ereditato dalla classe base.
Prevenzione di un’ulteriore derivazione
È possibile impedire che altre classi ereditino da una data classe o da uno qualsiasi dei suoi membri, dichiarando tale classe o tale membro come sealed. Per ulteriori informazioni, vedere la classe Classi e membri delle classi astratte e sealed (Guida per programmatori C#).
Nascondere un membro di una classe base in una classe derivata
Una classe derivata può nascondere i membri di una classe base dichiarando dei membri con lo stesso nome e la stessa firma. Il modificatore new può essere utilizzato per indicare in modo esplicito che un membro non costituisce un override del membro della classe base. L'utilizzo del modificatore new non è necessario, tuttavia, quando il modificatorenew non viene utilizzato, il compilatore genererà un avviso. Per ulteriori informazioni, vedere°Controllo delle versioni con le parole chiave Override e New (Guida per programmatori C#) e Sapere quando utilizzare le parole chiave Override e New (Guida per programmatori C#).
Vedere anche
Concetti
Riferimenti
Classi e strutture (Guida per programmatori C#)
Cronologia delle modifiche
Data |
Cronologia |
Motivo |
---|---|---|
Luglio 2008 |
Contenuto, un'illustrazione e nuovi esempi aggiunti. |
Miglioramento delle informazioni. |