Vererbung (C#-Programmierhandbuch)
Aktualisiert: August 2010
Vererbung ist, gemeinsam mit Kapselung und Polymorphie, eines der drei primären Merkmale (oder Pfeiler) der objektorientierten Programmierung. 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 über eine direkte Basisklasse verfügen. Allerdings ist die Vererbung transitiv. Wenn ClassC von ClassB und ClassB von ClassA abgeleitet wird, erbt ClassC die in ClassB und ClassA deklarierten Member.
Tipp
Strukturen unterstützen keine Vererbung, sie können jedoch Schnittstellen implementieren. Weitere Informationen finden Sie unter Schnittstellen (C#-Programmierhandbuch).
Vom Konzept her ist eine abgeleitete Klasse eine Spezialisierung der Basisklasse. Eine Basisklasse Animal kann beispielsweise eine abgeleitete Klasse mit dem Namen Mammal und eine weitere abgeleitete Klasse mit dem Namen Reptile haben. Ein Mammal ist ein Animal, und ein Reptile ist ein Animal, jede abgeleitete Klasse stellt jedoch unterschiedliche Spezialisierungen der Basisklasse dar.
Wenn eine Klasse so definiert wird, dass sie von einer anderen Klasse abgeleitet wird, erhält die abgeleitete Klasse implizit alle Member der Basisklasse, mit Ausnahme ihrer Konstruktoren und Destruktoren. In der abgeleiteten Klasse wird dadurch der Code der Basisklasse wieder verwendet, ohne dass dieser erneut implementiert werden muss. In der abgeleiteten Klasse können zusätzliche Member hinzugefügt werden. Auf diese Weise erweitert die abgeleitete Klasse die Funktionalität der Basisklasse.
In der folgenden Abbildung ist die Klasse WorkItem dargestellt, die ein Arbeitselement in einem Geschäftsprozess darstellt. Wie alle Klassen ist diese Klasse von System.Object abgeleitet und erbt alle ihre Methoden. WorkItem fügt selbst fünf Elemente hinzu. Enthält auch einen Konstruktor, da Konstruktoren nicht geerbt werden. Klasse ChangeRequest erbt von WorkItem und stellt eine bestimmte Art von Arbeitsaufgabe dar. ChangeRequest fügt den Membern, die es von WorkItem und Object erbt, zwei weitere Member hinzu. Sie muss den einen eigenen Konstruktor hinzufügen und fügt auch originalItemID hinzu. Die Eigenschaft originalItemID aktiviert die ChangeRequest-Instanz, die der ursprünglichen WorkItem zugeordnet werden soll, für die die Änderungsanforderung gilt.
Klassenvererbung
Im folgenden Beispiel wird dargestellt, wie die in der vorherigen Abbildung veranschaulichten Klassenbeziehungen in C# ausgedrückt werden. Das Beispiel zeigt auch, wie WorkItem die virtuelle Methode Object.ToString überschreibt, und wie die Klasse ChangeRequest 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 in einer Basisklasse eine Methode als virtuell deklariert wird, kann die Methode in einer abgeleiteten Klasse mit einer eigenen Implementierung überschrieben werden. Wenn in einer Basisklasse ein Member als abstrakt deklariert wird, muss diese Methode in jeder nicht abstrakten Klasse, die direkt von dieser Klasse erbt, überschrieben werden. Wenn eine abgeleitete Klasse selbst abstrakt ist, erbt diese abstrakte Member, ohne sie zu implementieren. Abstrakte und virtuelle Member sind die Grundlage für Polymorphie, die das zweite primäre Merkmal der objektorientierten Programmierung darstellt. Weitere Informationen finden Sie unter Polymorphismus (C#-Programmierhandbuch).
Abstrakte Basisklassen
Eine Klasse kann als abstrakt deklariert werden, wenn eine direkte Instanziierung mit dem Schlüsselwort new verhindert werden soll. In diesem Fall kann die Klasse nur verwendet werden, wenn eine neue Klasse von ihr abgeleitet wird. Eine abstrakte Klasse kann eine oder mehrere Methodensignaturen enthalten, die selbst als abstrakt deklariert sind. Diese Signaturen geben die Parameter und den Rückgabewert an, haben jedoch keine Implementierung (Methodentext). Eine abstrakte Klasse muss keine abstrakte Member enthalten. Wenn eine Klasse jedoch abstrakte Member enthält, muss die Klasse als abstrakt deklariert sein. Abgeleitete Klassen, die selbst nicht abstrakt sind, müssen die Implementierung für jede abstrakte Methode aus einer abstrakten Basisklasse bereitstellen. Weitere Informationen finden Sie unter Abstrakte und versiegelte Klassen und Klassenmember (C#-Programmierhandbuch) und Entwurf abstrakter Klassen.
Schnittstellen
Eine Schnittstelle ist ein Referenztyp, der einer abstrakten Basisklasse ähnelt, die nur aus abstrakten Membern besteht. Wenn eine Klasse eine Schnittstelle implementiert, muss diese eine Implementierung für alle Member dieser Schnittstelle bereitstellen. Ein Klasse kann viele Schnittstellen implementieren, obwohl sie nur von einer einzigen direkten Basisklasse abgeleitet sein kann.
Schnittstellen werden verwendet, um spezifische Fähigkeiten für Klassen zu definieren, die nicht notwendigerweise in einer "ist ein"-Beziehung zu einander stehen. Beispielsweise kann die System.IEquatable<T>-Schnittstelle durch jede Klasse oder Struktur implementiert werden, die es Clientcode ermöglichen muss, zu ermitteln, ob zwei Objekte des Typs äquivalent sind (dabei definiert allerdings der Typ die Äquivalenz). IEquatable<T> impliziert nicht die gleiche Art nicht "ist ein"-Beziehung, die zwischen einer Basisklasse und einer abgeleiteten Klasse besteht (z. B. ist ein Mammal ein Animal). Weitere Informationen finden Sie unter Schnittstellen (C#-Programmierhandbuch).
Zugriff der abgeleiteten Klasse auf Member der Basisklasse
Eine abgeleitete Klasse hat Zugriff auf die öffentlichen, geschützten, internen und geschützten internen Member einer Basisklasse. Auch wenn eine abgeleitete Klasse die privaten Member einer Basisklasse erbt, kann sie nicht auf diese Member zugreifen. Diese privaten Member sind jedoch noch in der abgeleiteten Klasse vorhanden und können die gleichen Aufgaben übernehmen, die sie in der Basisklasse übernehmen würden. Wenn beispielsweise eine geschützte Basisklassenmethode auf ein privates Feld zugreift, muss dieses Feld in der abgeleiteten Klasse vorhanden sein, damit die vererbte Basisklassenmethode ordnungsgemäß funktioniert.
Verhindern weiterer Ableitung
Eine Klasse kann andere Klassen daran hindern, von ihr oder einem ihrer Member zu erben, indem sie oder der Member als versiegelt deklariert wird. Weitere Informationen finden Sie unter Abstrakte und versiegelte Klassen und Klassenmember (C#-Programmierhandbuch).
Verdecken von Basisklassenmembern in abgeleiteten Klassen
In einer abgeleiteten Klasse können Member der Basisklasse verdeckt werden, indem Member mit demselben Namen und derselben Signatur deklariert werden. Der new-Modifizierer kann verwendet werden, um explizit anzugeben, dass der Member nicht als Überschreibung des Basismembers vorgesehen ist. Die Verwendung von new ist nicht erforderlich. Es wird jedoch eine Compilerwarnung erstellt, wenn new nicht verwendet wird. Weitere Informationen finden Sie unter Versionsverwaltung mit den Schlüsselwörtern "override" und "new" (C#-Programmierhandbuch) und Wann müssen die Schlüsselwörter "override" und "new" verwendet werden? (C#-Programmierhandbuch).
Siehe auch
Referenz
Klassen und Strukturen (C#-Programmierhandbuch)
Konzepte
Änderungsprotokoll
Datum |
Versionsgeschichte |
Grund |
---|---|---|
August 2010 |
Beispiel wurde vereinfacht und Kommentare hinzugefügt, um die Klarheit zu erhöhen. |
Kundenfeedback. |