Wenn wir eine Klasse erstellen, die von einer abstrakten Klasse erbt, und wenn wir die geerbte abstrakte Klasse implementieren, warum müssen wir das Schlüsselwort override verwenden?
"Warum?" Fragen wie diese können schwer zu beantworten sein, da sie vage sind. Ich gehe davon aus, dass Ihre Frage lautet: "Welche Argumente könnten während des Sprachdesigns vorgebracht werden, um für die Position zu argumentieren, dass das overrideSchlüsselwort erforderlich ist ?"
Beginnen wir mit einem Schritt zurück. In einigen Sprachen, beispielsweise Java, sind Methoden standardmäßig virtuell und werden automatisch überschrieben. Die Designer von C # waren sich dessen bewusst und betrachteten es als kleinen Fehler in Java. C # ist nicht "Java mit den dummen herausgenommenen Teilen", wie einige gesagt haben, aber die Designer von C # wollten unbedingt aus den problematischen Designpunkten von C, C ++ und Java lernen und sie nicht in C # replizieren.
Die C # -Designer betrachteten das Überschreiben als mögliche Fehlerquelle. Schließlich ist es eine Möglichkeit, das Verhalten von vorhandenem, getestetem Code zu ändern , und das ist gefährlich. Überschreiben sollte nicht beiläufig oder versehentlich erfolgen. Es sollte von jemandem entworfen werden, der darüber nachdenkt . Aus diesem Grund sind Methoden standardmäßig nicht virtuell und Sie müssen angeben, dass Sie eine Methode überschreiben.
Das ist die grundlegende Argumentation. Wir können jetzt auf einige fortgeschrittenere Überlegungen eingehen.
Die Antwort von StriplingWarrior bietet einen guten ersten Anhaltspunkt für ein fortgeschritteneres Argument. Der Autor der abgeleiteten Klasse ist möglicherweise nicht über die Basisklasse informiert, beabsichtigt möglicherweise, eine neue Methode zu erstellen, und wir sollten nicht zulassen, dass der Benutzer versehentlich überschreibt .
Obwohl dieser Punkt vernünftig ist, gibt es eine Reihe von Gegenargumenten, wie zum Beispiel:
- Der Autor einer abgeleiteten Klasse hat die Verantwortung, alles über die Basisklasse zu wissen ! Sie verwenden diesen Code erneut und sollten die erforderliche Sorgfalt walten lassen, um diesen Code gründlich zu verstehen, bevor sie ihn erneut verwenden.
- In Ihrem speziellen Szenario ist die virtuelle Methode abstrakt. Es wäre ein Fehler, ihn nicht zu überschreiben, und daher ist es unwahrscheinlich, dass der Autor versehentlich eine Implementierung erstellt.
Lassen Sie uns dann ein noch weiter fortgeschrittenes Argument zu diesem Punkt vorbringen. Unter welchen Umständen kann der Autor einer abgeleiteten Klasse dafür entschuldigt werden, dass er nicht weiß, was die Basisklasse tut? Betrachten Sie dieses Szenario:
- Der Autor der Basisklasse erstellt eine abstrakte Basisklasse B.
- Der abgeleitete Klassenautor in einem anderen Team erstellt mit Methode M eine abgeleitete Klasse D.
- Der Autor der Basisklasse erkennt, dass Teams, die die Basisklasse B erweitern, immer eine Methode M angeben müssen, sodass der Autor der Basisklasse die abstrakte Methode M hinzufügt.
- Was passiert, wenn Klasse D neu kompiliert wird?
Wir möchten, dass der Autor von D darüber informiert wird, dass sich etwas Relevantes geändert hat . Das Relevante, was sich geändert hat, ist, dass M jetzt eine Anforderung ist und dass ihre Implementierung überlastet werden muss. DM muss möglicherweise sein Verhalten ändern, sobald wir wissen, dass es von der Basisklasse aufgerufen werden kann. Das Richtige ist, nicht still zu sagen "Oh, DM existiert und erweitert BM". Das Richtige für den Compiler ist, fehlzuschlagen und zu sagen: "Hey, Autor von D, überprüfen Sie diese Annahme, die nicht mehr gültig ist, und korrigieren Sie gegebenenfalls Ihren Code."
In Ihrem Beispiel : Angenommen , das overridewar optional auf , SayHelloweil es eine abstrakte Methode überschreibt. Es gibt zwei Möglichkeiten: (1) Der Autor des Codes beabsichtigt, eine abstrakte Methode zu überschreiben, oder (2) die überschreibende Methode überschreibt versehentlich, weil jemand anderes die Basisklasse geändert hat und der Code jetzt auf subtile Weise falsch ist. Wir können diese Möglichkeiten nicht auseinanderhalten, wenn dies overrideoptional ist .
Wenn overridees jedoch erforderlich ist, können wir drei Szenarien unterscheiden. Wenn es einen möglichen Fehler im Code overridegibt, fehlt dieser . Wenn es absichtlich überschrieben wird, overrideist es vorhanden . Und wenn es absichtlich nicht überschrieben wird, dann newist es vorhanden . Das Design von C # ermöglicht es uns, diese subtilen Unterscheidungen zu treffen.
Denken Sie daran , dass das Melden von Compilerfehlern das Lesen der Gedanken des Entwicklers erfordert . Der Compiler muss aus falschem Code ableiten , welchen korrekten Code der Autor wahrscheinlich im Sinn hatte , und einen Fehler angeben, der ihn in die richtige Richtung weist. Je mehr Hinweise wir den Entwickler dazu bringen können, im Code zu hinterlassen, was er gedacht hat, desto besser kann der Compiler Fehler melden und desto schneller können Sie Ihre Fehler finden und beheben.
Im Allgemeinen wurde C # jedoch für eine Welt entwickelt, in der sich der Code ändert . Viele Funktionen von C #, die "ungerade" erscheinen, sind tatsächlich vorhanden, weil sie den Entwickler informieren, wenn eine früher gültige Annahme ungültig geworden ist, weil sich eine Basisklasse geändert hat. Diese Fehlerklasse wird als "spröde Basisklassenfehler" bezeichnet, und C # bietet eine Reihe interessanter Abhilfemaßnahmen für diese Fehlerklasse.