1. Einführung
Falls du jetzt ein bisschen ins Schwitzen kommst beim Wort Überschreiben, keine Panik — das ist gar nicht schlimm, sondern sogar ziemlich praktisch! Methodenüberschreibung bedeutet, dass du das Verhalten einer Methode aus der Basisklasse in einer abgeleiteten Klasse durch dein eigenes ersetzen kannst. Dadurch wird unser Code flexibel, erweiterbar und bereit für das echte Leben, wo jedes Tier garantiert nicht einfach nur "irgendein Geräusch" machen will.
Damit eine Methode überschrieben werden kann, markiert die Basisklasse sie mit dem Schlüsselwort virtual. Die abgeleitete Klasse ersetzt die Implementierung mit dem Schlüsselwort override.
Beispiel
public class Animal
{
public string Name { get; set; }
public virtual void MakeSound()
{
Console.WriteLine("Ein universelles Tiergeräusch...");
}
}
Und jetzt in der Klasse Hund:
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Wuff-wuff!");
}
}
- In der Basisklasse — virtual.
- In der abgeleiteten Klasse — override.
- Die Methodensignatur (Name, Rückgabetyp, Parameter) muss übereinstimmen.
Visuelles Diagramm
2. Demonstration der Funktionsweise
Lass uns anschauen, wie das Überschreiben in der Praxis funktioniert. Wir erstellen Tiere und prüfen das Geräusch:
Animal pet1 = new Animal { Name = "Namenloses Tier" };
Dog pet2 = new Dog { Name = "Barbos" };
pet1.MakeSound(); // Gibt aus: Ein universelles Tiergeräusch...
pet2.MakeSound(); // Gibt aus: Wuff-wuff!
Jetzt machen wir es etwas spannender:
Was passiert, wenn wir einen Hund in einer Variable vom Typ Animal speichern?
Animal pet3 = new Dog { Name = "Sharik" };
pet3.MakeSound(); // ???
Was meinst du, was passiert?
Antwort: Es wird "Wuff-wuff!" ausgegeben
Denn auch wenn die Variable vom Typ Animal ist, "zeigt" sie auf einen Hund, und die überschriebene Version der Methode wird aufgerufen!
Das ist die Magie des dynamischen (oder späten) Bindings.
3. Das Schlüsselwort base beim Überschreiben verwenden
Manchmal willst du die Implementierung einer Methode nicht komplett ersetzen, sondern erweitern — zum Beispiel etwas Eigenes hinzufügen und dann noch das alte Verhalten ausführen. Dafür gibt es das Schlüsselwort base. Damit kannst du die Version der Methode aus der Basisklasse aufrufen.
public class Cat : Animal
{
public override void MakeSound()
{
base.MakeSound(); // Aufruf der Basis-Implementierung
Console.WriteLine("Miau!");
}
}
Beim Aufruf dieser Methode steht zuerst "Ein universelles Tiergeräusch...", und dann "Miau!"
4. Wie die Methodenwahl beim Überschreiben funktioniert
Um anschaulich zu verstehen, was "unter der Haube" passiert, stell dir so eine Tabelle vor (virtuelles Dispatching):
| Variablentyp | Objekttyp | Welche Methode wird aufgerufen |
|---|---|---|
| Animal | Animal | Animal.MakeSound |
| Animal | Dog | Dog.MakeSound |
| Animal | Labrador | Labrador.MakeSound |
| Dog | Labrador | Labrador.MakeSound |
| Dog | Dog | Dog.MakeSound |
Die wichtigste Regel:
Der Variablentyp ist nur für den Compiler wichtig, zur Laufzeit zählt der tatsächliche Objekttyp (also das, was wir mit new erstellt haben).
Dieser Mechanismus heißt dynamisches (oder spätes) Binding — darauf basiert Polymorphismus (mehr dazu in der nächsten Vorlesung!).
Warum Methoden überschreiben?
- In GUI-Frameworks: Du hast eine Basisklasse für Fenster und überschreibst Methoden, um bestimmte Elemente zu zeichnen.
- In Game Engines: Basisklasse Enemy, abgeleitete Klassen implementieren verschiedene Verhaltensweisen.
- In Unit-Tests: Du kannst "Stubs" (stubs, mocks) für Methoden erstellen.
Moderne .NET-Frameworks nutzen diesen Mechanismus intensiv für Events, Template-Code, Vererbung von Konfigurationen und sogar für die Serialisierung von Objekten (z.B. über virtuelle Properties).
5. Das Schlüsselwort new beim Verstecken von Methoden
Wir haben schon festgestellt, dass man zum Überschreiben von Methoden das Duo virtual/override braucht. Aber in C# gibt es noch einen weiteren Modifikator, der mit Methoden in Vererbungshierarchien zu tun hat — das ist new.
Wozu new?
new wird verwendet, wenn du in einer abgeleiteten Klasse eine Methode mit derselben Signatur wie in der Basisklasse deklarierst, ABER du willst die virtuelle Methode nicht überschreiben, sondern sie verstecken (maskieren).
- Das ist kein Überschreiben, sondern Verstecken.
- So eine Methode wird nach dem Variablentyp aufgerufen, nicht nach dem tatsächlichen Objekttyp (kein dynamischer Polymorphismus!).
- Der Compiler warnt dich, wenn du "aus Versehen" eine Methode ohne das Schlüsselwort new versteckst.
Beispiel: Unterschied zwischen override und new
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Das Tier macht irgendein Geräusch...");
}
}
public class Dog : Animal
{
// Versteckt die Methode der Basisklasse (KEIN Überschreiben)
public new void MakeSound()
{
Console.WriteLine("Das ist kein override! Einfach eine Hundemethode.");
}
}
Jetzt schauen wir uns das Verhalten an:
Animal a = new Dog();
Dog d = new Dog();
a.MakeSound(); // "Das Tier macht irgendein Geräusch..."
d.MakeSound(); // "Das ist kein override! Einfach eine Hundemethode."
- Wenn die Variable vom Typ Dog ist — wird die Methode aus Dog aufgerufen.
- Wenn die Variable vom Typ Animal ist, auch wenn sie einen Dog enthält — wird die Methode aus Animal aufgerufen!
6. Feedback und Besonderheiten der Implementierung
Gerade am Anfang beim Programmieren kommt es oft zu Missverständnissen, wenn eine Methode "überschrieben" ist, aber trotzdem das alte Verhalten zeigt. Meistens ist der Grund einfach: In der Basisklasse fehlt virtual, oder in der abgeleiteten Klasse wurde die Methode mit new statt mit override deklariert. Der zweite Fall ist besonders tückisch — wenn du die Methode über eine Variable vom Basistyp aufrufst, wird die Basisversion aufgerufen, nicht die überschriebene. Also immer gut auf die richtigen Schlüsselwörter achten!
Neben Syntaxfehlern versuchen Anfänger manchmal, beim Überschreiben den Rückgabetyp zu ändern. Zum Beispiel in der Basisklasse object zurückgeben und in der abgeleiteten string. Das geht nicht: Die Methodensignatur muss komplett übereinstimmen.
Vergleichstabelle: override vs new
| Besonderheit | override | new |
|---|---|---|
| Mechanismus | Überschreibt eine virtuelle Methode | Versteckt die Methode der Basisklasse |
| Spätes Binding | Ja — funktioniert über dynamischen Polymorphismus | Nein — funktioniert nach Variablentyp |
| Erfordert in base... | virtual, abstract oder schon override | Nein |
| Empfohlen zu verwenden | Ja, fast immer | Nur in Ausnahmefällen |
7. Überschriebene Methoden und Vererbungshierarchien
Das ganze Thema mit Überschreiben wird besonders spannend, wenn wir lange Vererbungsketten haben:
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Das Tier macht irgendwas...");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Wuff-wuff!");
}
}
public class Labrador : Dog
{
public override void MakeSound()
{
Console.WriteLine("Ich bin ein Labrador: wau-wau!");
}
}
Was passiert, wenn wir schreiben:
Animal pet = new Labrador();
pet.MakeSound(); // => "Ich bin ein Labrador: wau-wau!"
C# wählt immer die "tiefste" Implementierung der virtuellen Methode, die es auf dem Objekt gibt.
8. Typische Fehler beim Überschreiben von Methoden
Die Welt ist nicht perfekt, und Studierende (und auch erfahrene Entwickler!) machen manchmal Fehler. Lass uns gleich lernen, die häufigsten "Fettnäpfchen" zu vermeiden:
1. virtual in der Basisklasse vergessen
public class Animal
{
public void MakeSound() { ... } // Kein 'virtual'
}
public class Dog : Animal
{
// Compilerfehler! Kann nicht überschreiben.
public override void MakeSound()
{
Console.WriteLine("Wuff!");
}
}
C# sagt sofort: 'Dog.MakeSound()': no suitable method found to override
2. Signaturen stimmen nicht überein
Achte darauf, dass Methodenname, Rückgabetyp und Parameter übereinstimmen:
public class Animal
{
public virtual void MakeSound() { ... }
}
public class Dog : Animal
{
// Fehler: Signatur ist anders (z.B. Parameter hinzugefügt)
public override void MakeSound(string sound)
{
Console.WriteLine(sound);
}
}
3. Verwende new nicht statt override ohne Grund
Das Schlüsselwort new erlaubt es, Methoden der Basisklasse zu verstecken, aber das ist kein Überschreiben und funktioniert nicht über dynamischen Polymorphismus. Das ist ein anderer Mechanismus, den man normalerweise ohne guten Grund vermeiden sollte.
GO TO FULL VERSION