Ereditarietà (Guida per programmatori C#)

L’ereditarietà, insieme all'incapsulamento e al 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. Classe derivata può disporre al massimo di una classe di base diretta. Tuttavia, l'ereditarietà è transitiva. Se ClassC viene derivata da ClassB e ClassB viene derivata da ClassA, ClassC eredita i membri dichiarati in ClassB e in ClassA.

Nota

Le strutture non supportano l'ereditarietà, mentre possono implementare interfacce. Per ulteriori informazioni, vedere 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. WorkItem aggiunge cinque suoi membri. Includono un costruttore, perché non vengono ereditati costruttori. La classe ChangeRequest eredita da WorkItem e rappresenta un particolare tipo di elemento di lavoro. ChangeRequest aggiunge altri due membri ai membri che eredita da WorkItem e da Object. Deve aggiungere il proprio costruttore e aggiunge anche originalItemID. La proprietà originalItemID consente all'istanza ChangeRequest di essere associata a WorkItem originale a cui la richiesta modifiche è applicata.

Ereditarietà delle classi

Ereditarietà di classe

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 the Object class.
public class WorkItem
{
    // Static field currentID stores the job ID of the last WorkItem that
    // has been created.
    private static int currentID;

    //Properties.
    protected int ID { get; set; }
    protected string Title { get; set; }
    protected string Description { get; set; }
    protected TimeSpan jobLength { get; set; }

    // Default constructor. If a derived class does not invoke a base-
    // class constructor explicitly, the default constructor is called
    // implicitly. 
    public WorkItem()
    {
        ID = 0;
        Title = "Default title";
        Description = "Default description.";
        jobLength = new TimeSpan();
    }

    // Instance constructor that has three parameters.
    public WorkItem(string title, string desc, TimeSpan joblen)
    {
        this.ID = GetNextID();
        this.Title = title;
        this.Description = desc;
        this.jobLength = joblen;
    }

    // Static constructor to initialize the static member, currentID. This
    // constructor is called one time, automatically, before any instance
    // of WorkItem or ChangeRequest is created, or currentID is referenced.
    static WorkItem()
    {
        currentID = 0;
    }


    protected int GetNextID()
    {
        // currentID is a static field. It is incremented each time a new
        // instance of WorkItem is created.
        return ++currentID;
    }

    // Method Update enables you to update the title and job length of an
    // existing WorkItem object.
    public void Update(string title, TimeSpan joblen)
    {
        this.Title = title;
        this.jobLength = joblen;
    }

    // Virtual method override of the ToString method that is inherited
    // from System.Object.
    public override string ToString()
    {
        return String.Format("{0} - {1}", this.ID, this.Title);
    }
}

// ChangeRequest derives from WorkItem and adds a property (originalItemID) 
// and two constructors.
public class ChangeRequest : WorkItem
{
    protected int originalItemID { get; set; }

    // Constructors. Because neither constructor calls a base-class 
    // constructor explicitly, the default constructor in the base class
    // is called implicitly. The base class must contain a default 
    // constructor.

    // Default constructor for the derived class.
    public ChangeRequest() { }

    // Instance constructor that has four parameters.
    public ChangeRequest(string title, string desc, TimeSpan jobLen,
                         int originalID)
    {
        // The following properties and the GetNexID method are inherited 
        // from WorkItem.
        this.ID = GetNextID();
        this.Title = title;
        this.Description = desc;
        this.jobLength = jobLen;

        // Property originalItemId is a member of ChangeRequest, but not 
        // of WorkItem.
        this.originalItemID = originalID;
    }
}

class Program
{
    static void Main()
    {
        // Create an instance of WorkItem by using the constructor in the 
        // base class that takes three arguments.
        WorkItem item = new WorkItem("Fix Bugs",
                                     "Fix all bugs in my code branch",
                                     new TimeSpan(3, 4, 0, 0));

        // Create an instance of ChangeRequest by using the constructor in
        // the derived class that takes four arguments.
        ChangeRequest change = new ChangeRequest("Change Base Class Design",
                                                 "Add members to the class",
                                                 new TimeSpan(4, 0, 0),
                                                 1);

        // Use the ToString method defined in WorkItem.
        Console.WriteLine(item.ToString());

        // Use the inherited Update method to change the title of the 
        // ChangeRequest object.
        change.Update("Change the Design of the Base Class",
            new TimeSpan(4, 0, 0));

        // 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 the Design of the 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 implementa 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 System.IEquatable<T>può essere implementata da qualunque classe o struct che deve abilitare 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 tipo di relazione "è" esistente tra una classe di base e una classe derivata (ad esempio, un Mammal è un Animal). Per ulteriori informazioni, vedere 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 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

Riferimenti

Classi e struct (Guida per programmatori C#)

class (Riferimenti per C#)

struct (Riferimenti per C#)

Concetti

Guida per programmatori C#

Cronologia delle modifiche

Data

Cronologia

Motivo

Agosto 2010

Semplificato l'esempio e aggiunti commenti per migliorare la chiarezza.

Commenti e suggerimenti dei clienti.