1. Einführung
Stell dir vor, du hast eine Basisklasse Animal mit einer Methode Speak(). Für alle Tiere schreibt sie standardmäßig "Irgendein Geräusch". Du willst jetzt abgeleitete Klassen wie Dog und Cat erstellen, damit sie nicht "Irgendein Geräusch" sagen, sondern ihre echten Sprüche ("Wuff!" und "Miau!").
Du könntest in jedem Nachfolger deine eigene Speak()-Methode schreiben, aber wenn du mit einer Collection vom Typ Animal[] arbeitest, wird der Aufruf animal.Speak() trotzdem die Methode von Animal nehmen und nicht die von Hund oder Katze – weil C# standardmäßig auf den Typ der Variable schaut, nicht auf das echte Objekt dahinter.
Hier ein Beispiel zum Anfassen:
Animal dog = new Dog();
dog.Speak(); // Oha, was wird ausgegeben?
Standardmäßig – "Irgendein Geräusch", nicht "Wuff!", selbst wenn Dog seine eigene Speak()-Methode hat. Was tun? Du musst die Methode als virtual (in der Basisklasse) und override (im Nachfolger) deklarieren.
Was ist eine virtuelle Methode?
virtual-Methode – das ist eine Methode, die in der Basisklasse mit dem Modifier virtual deklariert wird, damit sie in Nachfolgern überschrieben werden kann ( override).Wenn du so eine Methode über eine Referenz auf die Basisklasse aufrufst, wird die Methode des "echten" Objekts ausgeführt – wie im echten Leben: Wenn du ein Tier zähmst und es ist eine Katze, dann sagt es "Miau!" und nicht "Irgendein Geräusch".
Virtuelle Methoden sind das Fundament von Polymorphismus in C#.
2. Virtuelle Methoden deklarieren
Wie deklariert man eine virtuelle Methode
In der Basisklasse deklarierst du die Methode mit dem Schlüsselwort virtual:
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Irgendein Geräusch");
}
}
Im Nachfolger benutzt du das Schlüsselwort override:
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Wuff!");
}
}
public class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("Miau!");
}
}
Beispiel: Polymorphismus in Aktion
// Zum Beispiel in der Datei Program.cs
// Basisklasse Animal mit virtueller Methode Speak
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Irgendein Geräusch");
}
}
// Klasse Dog überschreibt das Verhalten von Speak
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Wuff!");
}
}
// Klasse Cat überschreibt auch Speak
public class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("Miau!");
}
}
public static class Program
{
public static void Main()
{
Animal[] animals = new Animal[]
{
new Animal(),
new Dog(),
new Cat()
};
foreach (Animal animal in animals)
{
animal.Speak(); // Gibt aus: "Irgendein Geräusch", "Wuff!", "Miau!"
}
}
}
Siehst du, was für Magie eine virtuelle Methode hat? Selbst wenn die Variable vom Typ Animal ist, wird die Methode der konkreten Implementierung aufgerufen.
3. Was passiert "unter der Haube": virtuelle Tabelle
Kurz zum Mechanismus
Wenn du eine Methode als virtuell deklarierst, fügt der Compiler sie in eine spezielle "virtuelle Tabelle" von Methoden für jeden Typ ein (fast wie eine Speisekarte in der Mensa für jedes Gericht). Beim Aufruf schaut C# nicht auf den Typ der Variable, sondern auf das, was wirklich hinter dem Verweis steckt – und "liefert" die Methode, die dem echten Typ des Objekts entspricht.
Man kann sagen, C# liefert das passende Rezept statt dem Standard, wenn die Klasse ihre eigene Variante hat.
Analogie: Vertrag mit Möglichkeit "Kleingedrucktes hinzuzufügen"
Stell dir vor, du hast einen Mietvertrag (Basisklasse), und darin steht, dass der Mieter standardmäßig eine feste Summe pro Monat zahlt (Methode PayRent()). Aber du bist ein schlauer Vermieter und erlaubst in den Anhängen zum Vertrag besondere Bedingungen (virtuelle Methode). Wenn einer der Mieter (Nachfolger) dieses Recht nutzt, schreibt er sein eigenes override und jetzt wird die Miete auf besondere Weise berechnet.
4. Wann sollte man virtuelle Methoden verwenden?
- Wenn die Methode in der Basisklasse für die meisten Nachfolger typisch funktionieren soll, aber manche ihr eigenes Verhalten brauchen.
- Wenn du eine Klassenhierarchie baust, bei der du Erweiterung oder Änderung von Teilen der Logik erwartest.
- Wenn du eine flexible Architektur entwickelst, z.B. für Business-Logik, wo verschiedene Operationstypen individuelles Verhalten brauchen.
In echten Projekten sind virtuelle Methoden das Fundament der Patterns Template Method (Schablonenmethode), Strategy (Strategie) und überhaupt der halben OOP-Welt.
Vergleich mit normalen Methoden
| Normale Methode | Virtuelle Methode | |
|---|---|---|
| Kann überschrieben werden | Nein | Ja (override) |
| Wie wird sie aufgerufen | Nach Typ der Variable | Nach "echtem" Typ des Objekts |
| Polymorphismus | Nein | Ja |
Häufige Fragen
- Kann ein Konstruktor virtuell sein?
Nein! Konstruktoren sind nie virtuell – sie funktionieren immer strikt nach dem Typ des Objekts. - Können virtuelle Methoden statisch sein?
Nein, nur Instanzmethoden können virtuell sein. Statische Methoden machen bei Polymorphismus nicht mit, weil es kein Objekt gibt – wer soll dann überschreiben? - Können Felder virtuell sein?
Nein, nur Methoden, Properties und Events.
5. Praxis: Wir entwickeln die App "Tierwelt" weiter
In unserer Lern-App hast du schon eine einfache Tier-Hierarchie gebaut. Lass uns den Tieren neue virtuelle Methoden geben, damit verschiedene Tiere unterschiedlich essen können!
Beispielcode mit Kommentaren
// In der Basisklasse definieren wir die virtuelle Methode Eat
public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Irgendein Geräusch");
}
public virtual void Eat()
{
Console.WriteLine("Isst generisches Futter.");
}
}
// In Dog überschreiben wir Eat
public class Dog : Animal
{
public override void Speak() => Console.WriteLine("Wuff!");
public override void Eat() => Console.WriteLine("Isst Hundefutter.");
}
// In Cat auch eigenes Verhalten
public class Cat : Animal
{
public override void Speak() => Console.WriteLine("Miau!");
public override void Eat() => Console.WriteLine("Isst Fisch.");
}
public static class Program
{
public static void Main()
{
Animal[] animals = new Animal[]
{
new Animal(),
new Dog(),
new Cat()
};
foreach (var animal in animals)
{
animal.Speak();
animal.Eat();
Console.WriteLine("---");
}
}
}
Fertig, jetzt essen alle Kätzchen und Hundchen auf ihre eigene Art! Und der Basis-Animal isst weiter irgendwas Komisches, wie ein echter Default-Case.
6. Typische Fehler und Besonderheiten
- Man kann override im Nachfolger vergessen – dann bleibt die Methode der Basisklasse aktiv.
- Oder umgekehrt, man vergisst virtual in der Basisklasse, dann kann man im Nachfolger kein override schreiben.
Du kannst kein override ohne virtual schreiben
Der C#-Compiler lässt dich keine Methode überschreiben, die nicht als virtuell (oder abstrakt) in der Basisklasse deklariert wurde.
Wozu braucht man override
override sagt dem Compiler und dem Leser klar: "Ich schreibe nicht einfach eine neue Methode – ich ändere bewusst das, was in der Basisklasse vorgegeben ist". Das verhindert Fehler, wenn du eigentlich "überschreiben" willst, aber versehentlich nur eine neue Methode mit ähnlicher Signatur schreibst.
Wenn du override vergisst und eine neue Methode schreibst
public class Dog : Animal
{
public void Speak()
{
Console.WriteLine("Wuff!");
}
}
Aber das ist kein Überschreiben! Wenn du auf Dog über eine Variable vom Typ Animal zugreifst, wird die Methode vom Tier ("Irgendein Geräusch") aufgerufen, nicht die vom Hund. Diese Überraschung mit den Typen!
GO TO FULL VERSION