Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Polymorphismus wird häufig nach der Kapselung und der Vererbung als die dritte Säule der objektorientierten Programmierung bezeichnet. Es handelt sich dabei um ein griechisches Wort, das "Vielgestaltigkeit" bedeutet und zwei verschiedene Aspekte umfasst:
- Zur Laufzeit können Objekte einer abgeleiteten Klasse an Orten wie Methodenparametern und Auflistungen oder Arrays als Objekte einer Basisklasse behandelt werden. Wenn diese Polymorphie auftritt, entspricht der deklarierte Typ des Objekts nicht mehr dem Runtimetyp.
- Basisklassen können virtuelleMethoden definieren und implementieren, und abgeleitete Klassen können sie überschreiben , was bedeutet, dass sie ihre eigene Definition und Implementierung bereitstellen. Zur Laufzeit, wenn die Methode von Clientcode aufgerufen wird, sucht die CLR den Laufzeittyp des Objekts und ruft die Überschreibung der virtuellen Methode auf. In Ihrem Quellcode können Sie eine Methode in einer Basisklasse aufrufen und bewirken, dass die Methodenversion der abgeleiteten Klasse ausgeführt wird.
Dank virtueller Methoden können Sie auf einheitliche Weise mit Gruppen verwandter Objekte arbeiten. Nehmen Sie beispielsweise an, Sie haben eine Zeichenanwendung, mit der ein Benutzer verschiedene Arten von Formen auf einer Zeichenoberfläche erstellen kann. Sie wissen zur Kompilierzeit nicht, welche bestimmten Arten von Formen ein*e Benutzer*in erstellt. Die Anwendung muss jedoch alle verschiedenen Formentypen, die erstellt werden, nachverfolgen und diese als Antwort auf die Mausaktionen des Benutzers aktualisieren. Sie können Polymorphismus verwenden, um dieses Problem mithilfe von zwei einfachen Schritten zu lösen:
- Erstellen Sie eine Klassenhierarchie, in der jede spezifische Formenklasse von einer gemeinsamen Basisklasse abgeleitet wird.
- Verwenden Sie eine virtuelle Methode, um die entsprechende Methode in einer abgeleiteten Klasse durch einen einzigen Aufruf der Basisklassenmethode aufzurufen.
Erstellen Sie zuerst eine Basisklasse namens Shape und abgeleitete Klassen, wie z. B. Rectangle, Circle und Triangle. Geben Sie der Shape-Klasse eine virtuelle Methode namens Draw, und überschreiben Sie sie in jeder abgeleiteten Klasse, um die jeweilige Form zu zeichnen, die die Klasse darstellt. Erstellen Sie ein List<Shape>-Objekt, und fügen Sie die abgeleiteten Klassen Circle, Triangle und Rectangle hinzu.
public class Shape
{
// A few example members
public int X { get; init; }
public int Y { get; init; }
public int Height { get; init; }
public int Width { get; init; }
// Virtual method
public virtual void Draw()
{
Console.WriteLine("Performing base class drawing tasks");
}
}
public class Circle : Shape
{
public override void Draw()
{
// Code to draw a circle...
Console.WriteLine("Drawing a circle");
base.Draw();
}
}
public class Rectangle : Shape
{
public override void Draw()
{
// Code to draw a rectangle...
Console.WriteLine("Drawing a rectangle");
base.Draw();
}
}
public class Triangle : Shape
{
public override void Draw()
{
// Code to draw a triangle...
Console.WriteLine("Drawing a triangle");
base.Draw();
}
}
Um die Zeichenoberfläche zu aktualisieren, verwenden Sie eine foreach-Schleife, um die Liste zu durchlaufen und die Draw-Methode für jedes Shape-Objekt in der Liste aufzurufen. Obwohl jedes Objekt in der Liste einen deklarierten Shape-Typ aufweist, wird der Runtimetyp (die überschriebene Version der Methode in jeder abgeleiteten Klasse) aufgerufen.
// Polymorphism at work #1: a Rectangle, Triangle and Circle
// can all be used wherever a Shape is expected. No cast is
// required because an implicit conversion exists from a derived
// class to its base class.
List<Shape> shapes =
[
new Rectangle(),
new Triangle(),
new Circle()
];
// Polymorphism at work #2: the virtual method Draw is
// invoked on each of the derived classes, not the base class.
foreach (var shape in shapes)
{
shape.Draw();
}
/* Output:
Drawing a rectangle
Performing base class drawing tasks
Drawing a triangle
Performing base class drawing tasks
Drawing a circle
Performing base class drawing tasks
*/
In C# ist jeder Typ polymorph, da alle Typen, einschließlich benutzerdefinierten Typen, von Object erben.
Übersicht über Polymorphie
Virtuelle Member
Wenn eine abgeleitete Klasse von einer Basisklasse erbt, enthält sie alle Member der Basisklasse. Das gesamte in der Basisklasse deklarierte Verhalten ist Teil der abgeleiteten Klasse. Dadurch können Objekte der abgeleiteten Klasse als Objekte der Basisklasse behandelt werden. Zugriffsmodifizierer (public, protected, privateusw.) bestimmen, ob auf diese Member über die abgeleitete Klassenimplementierung zugegriffen werden kann. Virtuelle Methoden bieten dem Designer verschiedene Auswahlmöglichkeiten für das Verhalten der abgeleiteten Klasse:
- Die abgeleitete Klasse kann virtuelle Member in der Basisklasse überschreiben und neues Verhalten definieren.
- Die abgeleitete Klasse kann die nächstgelegene Basisklassenmethode erben, ohne sie außer Kraft zu setzen, wobei das vorhandene Verhalten beibehalten wird, aber die Außerkraftsetzung der Methode durch weitere abgeleitete Klassen ermöglicht wird.
- Die abgeleitete Klasse kann eine neue nicht virtuelle Implementierung dieser Member definieren, die die Basisklassenimplementierungen ausblenden.
Eine abgeleitete Klasse kann einen Basisklassenmember nur überschreiben, wenn der Basisklassenmember als virtuell oder abstrakt deklariert ist. Der abgeleitete Member muss das override-Schlüsselwort verwenden, um explizit anzugeben, dass die Methode an dem virtuellen Aufruf beteiligt sein soll. Der folgende Code veranschaulicht dies:
public class BaseClass
{
public virtual void DoWork() { }
public virtual int WorkProperty => 0;
}
public class DerivedClass : BaseClass
{
public override void DoWork() { }
public override int WorkProperty
{
get { return 0; }
}
}
Anders als Methoden, Eigenschaften, Ereignisse und Indexer können Felder nicht virtuell sein. Wenn eine abgeleitete Klasse einen virtuellen Member überschreibt, wird dieser Member auch dann aufgerufen, wenn auf eine Instanz dieser Klasse als Instanz der Basisklasse zugegriffen wird. Der folgende Code veranschaulicht dies:
DerivedClass B = new();
B.DoWork(); // Calls the new method.
BaseClass A = B;
A.DoWork(); // Also calls the new method.
Mithilfe virtueller Methoden und Eigenschaften können abgeleitete Klassen eine Basisklasse erweitern, ohne die Basisklassenimplementierung einer Methode verwenden zu müssen. Weitere Informationen finden Sie unter Versionsverwaltung mit den Schlüsselwörtern „override“ und „new“. Eine Schnittstelle bietet eine weitere Möglichkeit zur Definition einer Methode bzw. einer Gruppe von Methoden, deren Implementierung von abgeleiteten Klassen übernommen wird.
Ausblenden von Basisklassenmembern für neue Member
Wenn Sie möchten, dass Ihre abgeleiteten Klassen einen Member mit demselben Namen wie ein Member in einer Basisklasse enthalten, können Sie das Schlüsselwort new verwenden, um den Basisklassenmember auszublenden. Das new Schlüsselwort wird vor dem Rückgabetyp eines Klassenmitglieds eingefügt, das ersetzt wird. Der folgende Code veranschaulicht dies:
public class BaseClass
{
public void DoWork() { WorkField++; }
public int WorkField;
public int WorkProperty
{
get { return 0; }
}
}
public class DerivedClass : BaseClass
{
public new void DoWork() { WorkField++; }
public new int WorkField;
public new int WorkProperty
{
get { return 0; }
}
}
Wenn Sie das new Schlüsselwort verwenden, erstellen Sie eine Methode, die die Basisklassenmethode ausblendet , anstatt sie außer Kraft zu setzen . Dies unterscheidet sich von virtuellen Methoden. Beim Ausblenden der Methode hängt die methode, die aufgerufen wird, vom Kompilierungszeittyp der Variablen und nicht vom Laufzeittyp des Objekts ab.
Auf ausgeblendete Klassenmember kann möglicherweise vom Clientcode immer noch zugegriffen werden, indem die Instanz der abgeleiteten Klasse in eine Instanz der Basisklasse umgewandelt wird. Zum Beispiel:
DerivedClass B = new();
B.DoWork(); // Calls the new method.
BaseClass A = (BaseClass)B;
A.DoWork(); // Calls the old method.
In diesem Beispiel beziehen sich beide Variablen auf dieselbe Objektinstanz, die methode, die aufgerufen wird, hängt jedoch vom deklarierten Typ der Variablen ab: DerivedClass.DoWork() beim Zugriff über die DerivedClass Variable und BaseClass.DoWork() beim Zugriff über die BaseClass Variable.
Verhindern der Überschreibung virtueller Member durch abgeleitete Klassen
Virtuelle Member bleiben virtuell, unabhängig davon, wie viele Klassen zwischen dem virtuellen Element und der Klasse deklariert werden, die sie ursprünglich deklariert hat. Wenn die Klasse A ein virtuelles Element deklariert, und eine Klasse B von A ableitet, und eine Klasse C von B ableitet, erbt die Klasse C das virtuelle Element und könnte es überschreiben, unabhängig davon, ob die Klasse B eine Überschreibung für dieses Element deklariert hat. Der folgende Code veranschaulicht dies:
public class A
{
public virtual void DoWork() { }
}
public class B : A
{
public override void DoWork() { }
}
Eine abgeleitete Klasse kann die virtuelle Vererbung stoppen, indem das Überschreiben als versiegelt deklariert wird. Zum Beenden der Vererbung muss das Schlüsselwort sealed vor dem Schlüsselwort override in der Klassenmemberdeklaration eingefügt werden. Der folgende Code veranschaulicht dies:
public class C : B
{
public sealed override void DoWork() { }
}
Im vorherigen Beispiel ist die Methode DoWork für Klassen, die von C abgeleitet wurden, nicht mehr virtuell. Für Instanzen von C wird sie weiterhin als virtuell betrachtet, auch wenn die Instanzen in den B- oder A-Typ umgewandelt werden. Versiegelte Methoden können durch abgeleitete Klassen ersetzt werden, indem das Schlüsselwort new wie im folgenden Beispiel gezeigt verwendet wird:
public class D : C
{
public new void DoWork() { }
}
Wenn DoWork für D mithilfe einer Variable vom Typ D aufgerufen wird, wird in diesem Fall die neue DoWork-Methode aufgerufen. Wenn eine Variable vom Typ C, B oder A verwendet wird, um auf eine Instanz von D zuzugreifen, folgt ein Aufruf von DoWork den Regeln der virtuellen Vererbung und leitet diese Aufrufe zur Implementierung von DoWork in der Klasse C um.
Zugreifen auf virtuelle Basisklassenmember über abgeleitete Klassen
Eine abgeleitete Klasse, die eine Methode oder Eigenschaft ersetzt oder überschreibt, kann weiterhin mithilfe des base Schlüsselworts auf die Methode oder Eigenschaft der Basisklasse zugreifen. Der folgende Code veranschaulicht dies:
public class Base
{
public virtual void DoWork() {/*...*/ }
}
public class Derived : Base
{
public override void DoWork()
{
//Perform Derived's work here
//...
// Call DoWork on base class
base.DoWork();
}
}
Weitere Informationen finden Sie unter base.
Hinweis
Es wird empfohlen, dass virtuelle Member base die Basisklassenimplementierung dieses Elements in ihrer eigenen Implementierung aufrufen. Durch Zulassen des Basisklassenverhaltens kann sich die abgeleitete Klasse auf die Implementierung von Verhalten konzentrieren, das spezifisch für die abgeleitete Klasse ist. Wenn die Basisklassenimplementierung nicht aufgerufen wird, liegt es an der abgeleiteten Klasse, ihr Verhalten mit dem Verhalten der Basisklasse kompatibel zu machen.