Visual C# 2012 Grundkurs

Überschreiben von Methoden

LinkedIn Learning kostenlos und unverbindlich testen!

Jetzt testen Alle Abonnements anzeigen
C# verwendet beim Überschreiben sowie Verdecken von Methoden bei der Vererbung einen unterschiedlichen Ansatz zu Java. Dabei ist die Verwendung des Override-Keywords explizit notwendig, um eine Methode überschreiben zu können.

Transkript

In diesem Abschnitt widmen wir uns einer Frage, die im Zusammenhang mit der Vererbung auftaucht und Sie möglicherweise irgendwann beschäftigen wird. C# verhält sich nämlich anders als Java, was das Überschreiben von Methoden angeht. In Java ist jede Methode virtuell, also per Default, per Voreinstellung und wird in einer abgeleiteten Klasse automatisch überschrieben. Microsoft ist mit C# hier einen anderen Weg gegangen. Dafür gibt es gute Gründe und diese Gründe möchte ich in diesem Abschnitt darlegen. Zunächst einmal möchte ich das Problem skizzieren, um das es hier geht. Dazu lege ich eine Base Class an und die soll jetzt irgendwas tun "public void DoSomeWork" und die gibt jetzt irgend etwas auf der Konsole aus. Nennen wir das, was sie ausgibt, mal das Base Work. Dann kopiere ich die ganze Klasse mal, mach daraus die Derived Class, die von der Base Class abgeleitet ist und die gibt hier so ein Derived Work aus. Mithilfe dieses Konstrukts kann ich jetzt hergehen und kann sagen: Okay, pass auf. Ich habe hier eine Liste von Base Class Objekten, also so ein typisches, polymorphes Konstrukt. Dieser Liste füge ich jetzt verschiedene Objekte hinzu, mal von der Base Class und mal von der Derived Class. Also  erst mal das hier und dann eins von der Derived Class. Und dann kann ich in einer Schleife überall diese Elemente iterieren und kann jetzt hier dieses "DoWork" aufrufen. Das wäre unser Test-Code und jetzt würde man ja eigentlich erwarten, dass dieses "DoSomeWork" von der abgeleiteten Klasse, dieses DoSomeWork von der Basisklasse überschreibt. Also müsste einmal Base Work und einmal Derived Work am Bildschirm entstehen. Aber, es ist eben nicht so. Es kommt zweimal Base Work. Der Grund dafür ist, dass sich eben Microsoft dazu entschieden hat, hier vom sog. Hiding zu sprechen. Also ich will das noch mal ganz klarlegen, was dieses "Hiding" ist. Ich habe jetzt hier ein Objekt vom Typ "Derived Class" und wenn ich jetzt hier die Methode "Do Some Work" von dieser Derived Class aufrufe, dann erscheint hier dieses "Derived Work". Das bedeutet, wenn klar ist, dass ich mit einem Objekt der Derived Class zu tun habe, dann versteckt sozusagen diese Derived Class die Do Some Work der Basisklasse. Habe ich es aber mit Objekten vom Typ "Base Class" zu tun, dann geht .NET davon aus, dass ich auch die Methode "Do Some Work" von der Base Class benutzen möchte, und nicht die von der Derived Class. Tun wir das hier nochmal raus derweilen und jetzt haben wir wieder den Code von vorhin. Um jetzt das erwünschte Verhalten zu erhalten, muss ich also in der Basisklasse die Methode "Do Some Work" als "Virtual" markieren und zusätzlich hier als "Overwrite". Wenn ich das jetzt so aufrufen würde, hätte sich immer noch nichts geändert. Andererseits könnte ich diese Methode nicht als Overwrite" deklarieren, weil Overwrite erwartet, dass die Basisklasse diese Methode als "Virtual" markiert. Erst jetzt gibt sich der Compiler zufrieden, jetzt kommt auch keine Warnung mehr, und das Verhalten ist entsprechend, wie wir es erwarten, dass beim Objekt der abgeleiteten Klasse auch die Methode der abgeleiteten Klasse aufgerufen wird. So weit, so schön. Jetzt ist die Frage: Warum hat man das nicht wie bei Java gemacht, wo nämlich genau dieses Verhalten Standardverhalten ist und man diese Schlüsselworte eigentlich gar nicht braucht? Um dass zu verstehen, muss ich ein bisschen ausholen und vor allem ich muss mal so tun, als wäre die Basisklasse in einer Bibliothek, die wir von einem anderen Hersteller hinzugekauft hätten. Um das mal zu simulieren, lege ich hier so eine Class Library an die nenne ich jetzt mal "Base Lib" und verschiebe diese Basisklasse in die Base Lib. So, jetzt ist diese Base Class hier natürlich unbekannt, D.h., ich muss jetzt hier erstmal eine Referenz anlegen und sage okay, ich will eine Referenz zu dieser Base Lib und dann brauche ich hier natürlich noch den Namespace von dieser Base Lib, das ist eben Base Lib. Und jetzt ist die Basisklasse hier wieder bekannt und damit lässt sich schon besser arbeiten. Jetzt müssen wir nur hier nochmal diese Basisklasse auch public machen, weil sie muss ja jetzt über die Assembly-Grenzen hinweg sichtbar sein. Jetzt sollte eigentlich alles wieder so sein, wie vorher. Ich habe jetzt hier mein Software-System, das benutzt eben eine zugekaufte Bibliothek. Normalerweise habe ich keinen Einfluss auf den Source Code von einer zugekauften Bibliothek, kann also nicht bestimmen, was so eine Bibliothek genau zur Verfügung stellt und wie sie implementiert ist. Um jetzt besser zeigen zu können, was ich zeigen wollte, verpasse ich dieser Bibliothek mal noch eine zweite Klasse. Die nennen wir "Second Base Class" und die hat eine Methode "FU" und die tut jetzt hier irgendwelche interessanten Dinge. Im Augenblick beschränken wir uns wieder auf eine Ausgabe auf der Konsole, damit wir es wissen, was passiert, wenn die Methode aufgerufen wird. Hier verändere ich mal den ganzen aufrufenden Code. Also ich gehe noch mal zurück zu der Bibliothek, diese Methode FU, das ist ja der Witz daran, die soll nämlich ein Objekt vom Typ "Base Class" als Parameter übernehmen. Jetzt kehre ich wieder zurück zu meiner Applikation und schreibe mal vom typischen Code von so einer Applikation. Der würde jetzt also hier so ein Derived Class Objekt anlegen und kann so ein Objekt vom Typ "Second Base Class" anlegen und kann die Methode Fu aufrufen. Und diese Methode Fu, der kann man jetzt nicht nur ein Base Class Objekt übergeben, sondern der kann man auch ein Derived Class Objekt übergeben. Das ist sehr praktisch, weil so ein zugekauftes System, das lässt sich per Vererbung, durch zusätzliche Funktionalität erweitern. Und genau das kann ich jetzt hier mal zeigen. Ich schreibe hier jetzt einfach mal eine zusätzliche Methode dazu, "public void ProduceSomeSpecialWork", so soll diese Methode  mal heißen. Auch die gibt jetzt einfach mal was auf der Konsole aus und zwar "derived special work". Diese Funktionalität, die hat mir schon lange gefehlt an dieser Base Class und dadurch, dass ich die Derived Class von der Base Class ableite, kann ich diese Funktionalität jetzt hier implementieren. Und indem ich jetzt hier statt einer Base Class eine Derived Class in der Applikation verwende, habe ich also diese spezielle Funktionalität zur Verfügung. Das kann ich auch gleich mal zeigen, indem ich die Methode hier mal aufrufe. Jetzt rufen wir mal das Ganze auf und schauen mal, was passiert. Hier haben wir den ersten Aufruf. Das ist also diese Methode "DoSomeSpecialWork"und die gibt eben dies hier auf der Konsole aus und dann rufe ich von dieser Second Base Class die Methode Fu auf und die gibt eben dieses "Fu" auf der Konsole aus. Jetzt fällt dem Hersteller der Bibliothek ein: Er könnte mal wieder eine neue Version der Bibliothek machen. Und er sagt sich: "Mensch, was schon lange gefehlt hat, das wäre eine Methode wie die Folgende." Der Hersteller kreiert also eine Methode, die genau dieselbe Signatur hat, wie die Methode, die ich bereits in der abgeleiteten Klasse geschrieben habe nicht wissend, dass irgendein Anwender seiner Bibliothek bereits so eine Methode implementiert hat mit dem gleichen Namen wird also der Hersteller jetzt hier irgendeine Funktionalität implementieren. Die Methode heißt ja so ähnlich. Die wird etwas Ähnliches tun, aber es wird nicht auf die gleiche Weise geschehen. Das Verhalten wird ein unterschiedliches sein. Und das kann ich jetzt hier eben mal ausdrücken, indem ich hier "Base Special Work" ausgebe, um zu signalisieren, dass das die Methode "Special Work" ist aus der Base Class. Weil es so schön war, sagt sich der Hersteller: "Mensch, diese Methode "FU" hier, die könnten wir eigentlich wesentlich eleganter implementieren, wenn wir hier die Methode "DoSomeSpecialWork" von diesem Base Class Objekt aufrufen." Das ist also die neue Implementierung der Methode "FU" in der neuen Bibliothek. Weil ja ansonsten alle Interfaces hier gleichgeblieben sind, also alle Methoden die bislang da waren, haben die gleichen Signaturen und tun das Gleiche, deswegen verkauft der Hersteller diese Bibliothek als abwärtskompatibel zur vergangenen Version. Es ist nichts gestrichen und nichts verändert worden. Es ist nur etwas dazugekommen. Diese Methode eben. Ich als Hersteller meiner Applikation sage: "Wow. Das ist ja wunderbar. Diese neue Bibliothek nehmen wir her und ich kann eigentlich meine Applikation genauso lassen, wie sie war." Wenn ich sie jetzt aufrufe, dann passiert auch genau das, was passieren soll. Es ist also die gleiche Ausgabe wie vorhin. Ich habe diese Methode "FU" aufgerufen. Die Methode "FU" benutzt diese Special Work Methode von der Basisklasse und genau die wird auch aufgerufen. Wenn aber das System jetzt so wäre wie in Java, dann wäre ja hier implizit ein "Overwrite" implementiert worden. Und dann sähe die Sache nämlich völlig anders aus. Dann nämlich würde die Methode "Fu" statt dieses "Special Work" von der Basisklasse plötzlich dieses "Special Work" von der Derived- Klasse aufrufen. Und das ändert natürlich das Verhalten komplett. Der Hersteller der Bibliothek geht nämlich davon aus, dass Fu eben genau diese Methode aufruft. Das ist aber jetzt gar nicht mehr der Fall und damit könnte ein Benutzer einer Bibliothek unabsichtlich das Verhalten der Bibliothek verändern. Und genau das wollte Microsoft vermeiden, indem eben dieses Overwrite Keyword hier an der Stelle unabdingbar notwendig ist, um eine Methode zu überschreiben. Und wie gesagt, man kann da kein Overwrite Keyword jetzt profilaktisch vergeben. Es muss ja immer schon eine virtuelle Methode dazu da sein, damit man diese überschreiben kann. Auf diese Weise ist eigentlich eine sehr gute Sicherheit im System gegeben, dass ich nicht aus Versehen aus Vererbung irgendwelche seltsamen Effekte produziere. Das ist der Grund, warum Microsoft hier so restriktiv war.

Visual C# 2012 Grundkurs

Schreiben Sie eigene Programme in C# und lernen Sie dazu alle Schlüsselwörter und die meisten Konstrukte kennen, um sicher mit dieser Programmierspreche umzugehen.

7 Std. 1 min (44 Videos)
Derzeit sind keine Feedbacks vorhanden...

Dieser Online-Kurs ist als Download und als Streaming-Video verfügbar. Die gute Nachricht: Sie müssen sich nicht entscheiden - sobald Sie das Training erwerben, erhalten Sie Zugang zu beiden Optionen!

Der Download ermöglicht Ihnen die Offline-Nutzung des Trainings und bietet die Vorteile einer benutzerfreundlichen Abspielumgebung. Wenn Sie an verschiedenen Computern arbeiten, oder nicht den ganzen Kurs auf einmal herunterladen möchten, loggen Sie sich auf dieser Seite ein, um alle Videos des Trainings als Streaming-Video anzusehen.

Wir hoffen, dass Sie viel Freude und Erfolg mit diesem Video-Training haben werden. Falls Sie irgendwelche Fragen haben, zögern Sie nicht uns zu kontaktieren!