Vererbung (C#-Programmierhandbuch)

Die Vererbung ist, zusammen mit der Kapselung und der Polymorphie, eines der drei primären Charakteristika des objektorientierten Programmierens. Die Vererbung ermöglicht die Erstellung neuer Klassen, die in anderen Klassen definiertes Verhalten wieder verwenden, erweitern und ändern. Die Klasse, deren Member vererbt werden, wird Basisklasse genannt, und die Klasse, die diese Member erbt, wird abgeleitete Klasse genannt. Eine abgeleitete Klasse kann nur eine direkte Basisklasse haben. Doch die Vererbung ist transitiv. Wenn Klasse C von Klasse B abgeleitet wird, und Klasse B von Klasse A abgeleitet wird, erbt Klasse C die in Klasse B und A deklarierten Member.

Hinweis

Strukturen unterstützen die Vererbung nicht, aber sie können Schnittstellen implementieren. Weitere Informationen finden Sie unter Schnittstellen.

Konzeptuell gesehen ist die abgeleitete Klasse eine Spezialisierung der Basisklasse. Wenn Sie beispielsweise eine Basisklasse Animal haben, haben Sie möglicherweise eine abgeleitete Klasse mit dem Namen Mammal und eine andere abgeleitete Klasse mit dem Namen Reptile. Ein Mammal ist ein Animal, und ein Reptile ist ein Animal, aber jede abgeleitete Klasse repräsentiert unterschiedliche Spezialisierungen der Basisklasse.

Wenn Sie eine Klasse definieren, die von einer anderen Klasse abgeleitet werden soll, erhält die abgeleitete Klasse implizit alle Member der Basisklasse – ausgenommen davon sind deren Konstruktoren und Finalizern. Die abgeleitete Klasse kann so den Code der Basisklasse wiederverwenden, ohne diesen erneut implementieren zu müssen. Sie können der abgeleiteten Klasse mehr Member hinzufügen. Auf diese Art erweitert die abgeleitete Klasse die Funktionalität der Basisklasse.

Die folgende Abbildung zeigt eine Klasse WorkItem, die ein Arbeitselement in einem Geschäftsprozess repräsentiert. Wie alle Klassen leitet es sich von <xref:System.Object?displayProperty=fullName> ab und erbt dessen Methoden. WorkItem fügt von allein fünf Member hinzu. Diese beinhalten einen Konstruktor, weil Konstruktoren nicht vererbt werden. Die Klasse ChangeRequest erbt von WorkItem und repräsentiert eine bestimmt Art von Arbeitselementen. ChangeRequest fügt den von WorkItem und <xref:System.Object> geerbten Membern zwei weitere Member hinzu. Es muss seinen eigenen Konstruktor hinzufügen, und es fügt auch originalItemID hinzu. Die Eigenschaft originalItemID ermöglicht es der ChangeRequest-Instanz, mit dem ursprünglichen WorkItem verknüpft zu werden, für das die Änderungsanforderung gilt.

Klassenvererbung
Klassenvererbung

Das folgende Beispiel zeigt, wie die in der oben stehenden Abbildung veranschaulichten Klassenbeziehungen in C# ausgedrückt werden. Das Beispiel zeigt auch, wie WorkItem die virtuelle Methode <xref:System.Object.ToString%2A?displayProperty=fullName> außer Kraft setzt, und wie die ChangeRequest-Klasse die WorkItem-Implementierung der Methode erbt.

// 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
*/

Abstrakte und virtuelle Methoden

Wenn eine Basisklasse eine Methode als virtuell deklariert, kann eine abgeleitete Klasse die Methode mit ihrer eigenen Implementierung außer Kraft setzen. Wenn eine Basisklasse eine Methode als abstrakt deklariert, muss diese Methode in jeder nicht abstrakten Klasse außer Kraft gesetzt werden, die direkt von dieser Klasse erbt. Wenn eine abgeleitete Klasse selbst abstrakt ist, erbt sie abstrakte Member, ohne diese zu implementieren. Abstrakte und virtuelle Member sind die Basis für Polymorphie, die das zweite charakteristische Merkmal des objektorientierten Programmierens ist. Weitere Informationen finden Sie unter Polymorphie.

Abstrakte Basisklassen

Sie können eine Klasse als abstrakt deklarieren, wenn Sie die direkte Instanziierung mit dem Schlüsselwort new vermeiden möchten. Wenn Sie dies machen, kann die Klasse nur verwendet werden, wenn eine neue Klasse von ihr abgeleitet wird. Eine abstrakte Klasse kann mindestens eine Methodensignatur enthalten, die selbst auch als abstrakt deklariert wurden. Diese Signaturen geben die Parameter und Rückgabewerte an, verfügen aber über keine Implementierung (Methodenkörper). Eine abstrakte Klasse muss nicht zwangsläufig abstrakte Member enthalten; wenn eine Klasse allerdings einen abstrakten Member enthält, muss die Klasse an sich auch als abstrakt deklariert werden. Abgeleitete Klassen, die nicht selbst abstrakt sind, müssen eine Implementierung für jede beliebige abstrakte Methode aus einer abstrakten Basisklasse bereitstellen. Weitere Informationen zu abstrakten Klassen finden Sie unter Abstrakte und versiegelte Klassen und Klassenmember.

Schnittstellen

Eine Schnittstelle ist ein Verweistyp, der einer abstrakten Basisklasse leicht ähnelt, die nur aus zwei abstrakten Membern besteht. Wenn eine Klasse eine Schnittstelle implementiert, muss sie eine Implementierung für alle Member der Schnittstelle bereitstellen. Ein Klasse kann mehrere Schnittstellen implementieren, auch wenn sie nur von einer einzelnen direkten Basisklasse ableiten kann.

Schnittstellen werden verwendet, um bestimmte Funktionen zu definieren, die nicht unbedingt in einer „ist ein“-Beziehung zueinander stehen. Die <xref:System.IEquatable%601?displayProperty=fullName>-Schnittstelle kann z.B. von jeder beliebigen Klasse oder Struktur implementiert werden, die es Clientcode ermöglichen muss, ermitteln zu können, ob zwei Objekte des Typs äquivalent sind (allerdings definiert der Typ Äquivalenz). Animal impliziert nicht dieselbe Art einer „ist ein“-Beziehung, wie sie zwischen einer Basis- und einer abgeleiteten Klasse besteht (z.B. ist ein <xref:System.IEquatable%601> ein Mammal). Weitere Informationen finden Sie unter Schnittstellen.

Weitere Ableitung verhindern

Eine Klasse kann andere Klassen daran hindern, von ihr oder einem ihrer Member zu erben, indem sie sich selbst oder den Member als versiegelt deklariert. Weitere Informationen zu abstrakten Klassen finden Sie unter Abstrakte und versiegelte Klassen und Klassenmember.

Das Verbergen von Membern der Basisklasse in einer abgeleiteten Klasse

Eine abgeleitete Klasse kann Basisklassenmember verbergen, indem sie Member mit demselben Namen und derselben Signatur deklariert. Der Modifizierer new kann verwendet werden, um explizit anzugeben, dass der Member nicht dazu vorgesehen ist, den Basismember außer Kraft zu setzen. Das Verwenden von new ist nicht erforderlich, aber es wird eine Compilerwarnung generiert, wenn new nicht verwendet wird. Weitere Informationen finden Sie unter Versionsverwaltung mit den Schlüsselwörtern „override“ und „new“ und Wann müssen die Schlüsselwörter „override“ und „new“ verwendet werden?.

Siehe auch

C#-Programmierhandbuch
Klassen und Strukturen
class
struct