Warum ist das Überschreiben der statischen Methode in C # erlaubt?

7
%Vor%

Warum darfst du statische Methoden überschreiben? Nichts als Fehler kann daraus entstehen, es funktioniert nicht so, wie Sie denken würden.

Nimm die folgenden Klassen.

%Vor%

das ist erlaubt, und der Code

%Vor%

ist zwar erlaubt, tut aber nicht, was Sie denken würden, wenn Sie sich den Code ansehen.

ruft Logger.Log mit LogName = null auf.

Warum ist das erlaubt?

    
AndreasN 16.11.2010, 17:04
quelle

6 Antworten

24

Das Schlüsselwort new überschreibt keine Methode. Es erstellt stattdessen eine neue Methode des gleichen Namens, die unabhängig vom Original ist. Es ist nicht möglich, eine statische Methode zu überschreiben, da sie nicht virtuell sind.

    
JaredPar 16.11.2010, 17:07
quelle
15

Du übertreibst es nicht, du versteckst es. Eine normale Methode würde genau das gleiche Verhalten zeigen, so dass hier nichts spezifisch für statische Methoden ist.

Verstecken ist nur in wenigen Fällen sinnvoll. Der Bereich, in dem ich am häufigsten aufgetreten bin, macht den Rückgabetyp in einer abgeleiteten Klasse spezifischer. Aber ich hatte das nie mit statischen Methoden.

Ein Bereich, in dem statische Funktionen mit einem bestimmten Namen nützlich sein können, ist, wenn Sie Reflektionen verwenden und Informationen zu jeder Klasse erhalten möchten, indem Sie sie von einer Methode zurückgeben. Aber in den meisten Fällen passt ein Attribut besser.

Und es ist nicht wahrscheinlich, dass Fehler erzeugt werden, da Ihr Code eine Compiler-Warnung erzeugt:

  

Warnung 'StaticHiding.SpecificLogger.LogName' verbirgt das geerbte Element 'StaticHiding.BaseLogger.LogName'. Verwenden Sie das neue Schlüsselwort, wenn das Verstecken beabsichtigt war.

Und wenn Sie new verwenden, sollten Sie wissen, was Sie tun.

    
CodesInChaos 16.11.2010 17:07
quelle
9

Andere haben darauf hingewiesen, dass dies nicht der Vorrang ist, aber das hinterlässt immer noch Ihre ursprüngliche Frage: Warum können Sie das tun? (Aber die Frage ist wirklich "Warum können Sie statische Methoden ausblenden".)

Es ist eine unausweichliche Funktion, die unabhängige Versionierung von Komponenten zu unterstützen, die Basisklassen und Komponenten enthalten, die diese Basisklassen verwenden.

Stellen Sie sich zum Beispiel vor, dass die Komponente CompB die Basisklasse enthält und eine andere Komponente CompD eine abgeleitete Klasse enthält. In Version 1 von CompB wurde möglicherweise keine Eigenschaft namens LogName angegeben. Der Autor von CompD beschließt, eine statische Eigenschaft namens LogName hinzuzufügen.

Der kritische Punkt an dieser Stelle ist, dass der Autor von v1 von CompD kein Feature der Basisklasse ersetzen oder ausblenden wollte - es gab kein Mitglied namens LogName in der Basisklasse, als sie diesen Code geschrieben haben / p>

Stellen Sie sich nun vor, dass eine neue Version der CompB-Bibliothek veröffentlicht wurde. In dieser neuen Version hat der Autor eine LogName-Eigenschaft hinzugefügt. Was soll in CompD passieren? Die Optionen scheinen zu sein:

  1. CompD funktioniert nicht mehr, weil der eingeloggte LogName mit dem zu CompB
  2. hinzugefügten LogName kollidiert
  3. Irgendwie ersetzt der LogName der CompD die Basis CompB LogName. (Es ist nicht wirklich im Entferntesten klar, wie das mit Statik funktionieren könnte. Man könnte sich das jedoch mit Nicht-Statik vorstellen.)
  4. Behandle die beiden LogName-Mitglieder als völlig unterschiedliche Mitglieder, die zufällig denselben Namen haben. (In Wirklichkeit, sie nicht - sie heißen BaseLogger.LogName und SpecificLogger.LogName. Aber da in C # wir nicht immer den Mitgliedsnamen mit der Klasse zu qualifizieren haben, sieht es so aus, als wären sie derselbe Name. )

.NET entscheidet sich für 3. (Und das sowohl mit statischen als auch nicht-statischen Methoden. Wenn Sie das Verhalten 2 - Ersetzen - mit Nicht-Statik wollen, dann muss die Basis virtual und Die abgeleitete Klasse muss die Methode als override markieren, um deutlich zu machen, dass sie eine Methode in der Basisklasse absichtlich überschrieben hat.C # wird niemals die Methode einer abgeleiteten Klasse dazu bringen, die Methode einer Basisklasse zu ersetzen, sofern die abgeleitete Klasse nicht explizit angegeben wird dass dies das ist, was sie wollten.) Dies ist wahrscheinlich sicher, da die beiden Mitglieder nicht verwandt sind - der Basis-LogName existierte nicht einmal an dem Punkt, wo der abgeleitete Eins eingeführt wurde. Und dies ist vorzuziehen, einfach zu brechen, weil die neueste Version der Basisklasse ein neues Mitglied eingeführt hat.

Ohne diese Funktion ist es für neue Versionen von .NET Framework nicht möglich, neue Member zu vorhandenen Basisklassen hinzuzufügen, ohne dass dies eine wichtige Änderung darstellt.

Sie sagen, dass das Verhalten nicht das ist, was Sie erwarten. Eigentlich ist es genau das, was ich erwarten würde und was Sie wahrscheinlich in der Praxis wollen. Der BaseLogger hat keine Ahnung, dass der SpecificLogger eine eigene LogName-Eigenschaft eingeführt hat. (Es gibt keinen Mechanismus, mit dem es möglich wäre, weil Sie statische Methoden nicht überschreiben können.) Und wenn der Autor von SpecificLogger diese LogName-Eigenschaft geschrieben hat, denken Sie daran, dass sie gegen v1 von BaseLogger geschrieben haben, die keinen LogName hatten dass es die Basismethode entweder ersetzen sollte. Da keine Klasse Ersatz will, wäre der Austausch eindeutig falsch.

Das einzige Szenario, in dem Sie jemals in dieser Situation enden sollten, ist, weil die zwei Klassen in verschiedenen Komponenten sind. (Natürlich können Sie ein Szenario ersinnen, wenn sie sich in derselben Komponente befinden, aber warum sollten Sie das jemals tun? Wenn Sie beide Codeabschnitte besitzen und sie in einer einzigen Komponente veröffentlichen, wäre es immer verrückt, dies zu tun.) Und so sollte BaseLogger seine eigene LogName-Eigenschaft bekommen, was genau passiert. Sie haben vielleicht geschrieben:

%Vor%

aber der C # -Compiler sieht, dass SpecificLogger keine Log-Methode zur Verfügung stellt, also macht es das zu:

%Vor%

, weil hier die Log-Methode definiert ist.

Wenn Sie also eine Methode in einer abgeleiteten Klasse definieren, die nicht versucht, eine vorhandene Methode zu überschreiben, gibt der C # -Compiler dies in den Metadaten an. (In den Methodenmetadaten gibt es eine "newslot" -Einstellung, die besagt, dass diese Methode brandneu sein soll und nichts mit der Basisklasse zu tun haben soll.)

Aber das gibt Ihnen ein Problem, wenn Sie CompD neu kompilieren möchten. Nehmen wir an, Sie haben einen Fehlerbericht, der auf einen völlig unzusammenhängenden Code zurückzuführen ist, und Sie müssen eine neue Version von CompD veröffentlichen. Sie kompilieren es gegen die neue Version von CompB. Wenn der Code, den Sie geschrieben haben, nicht erlaubt war, wären Sie nicht in der Lage - alter Code, der bereits kompiliert wurde, würde funktionieren, aber Sie wären nicht in der Lage, neue Versionen dieses Codes zu kompilieren, was ein bisschen verrückt wäre .

Um dieses (offensichtliche etwas obskure) Szenario zu unterstützen, erlauben sie Ihnen dies zu tun. Sie erzeugen eine Warnung, um Sie wissen zu lassen, dass Sie hier einen Namenskonflikt haben. Sie müssen das new -Schlüsselwort angeben, um es loszuwerden.

Dies ist ein obskures Szenario, aber wenn Sie die Vererbung über Komponentengrenzen hinweg unterstützen möchten, benötigen Sie dies. Andernfalls wäre das Hinzufügen neuer öffentlicher oder geschützter Elemente in einer Basisklasse immer eine bahnbrechende Änderung. Deshalb ist das hier. Aber es ist eine schlechte Übung, sich jemals darauf zu verlassen, daher die Tatsache, dass Sie eine Compiler-Warnung erhalten. Die Verwendung des new -Schlüsselwortes, um die Warnung loszuwerden, sollte immer nur eine Notlösung sein.

Die Quintessenz ist folgende: Diese Funktion existiert nur aus einem Grund, und das ist, Sie aus einem Loch in Situationen zu bringen, in denen eine neue Version einer Basisklasse ein Mitglied hinzugefügt hat, das zuvor nicht existierte, und das kollidiert mit einem Mitglied, das bereits in Ihrer abgeleiteten Klasse ist. Wenn dies nicht der Fall ist, verwenden Sie diese Funktion nicht.

(Ich denke, sie sollten eher einen Fehler als eine Warnung ausgeben, wenn Sie neu auslassen, um dies deutlicher zu machen.)

    
Ian Griffiths 16.11.2010 17:32
quelle
2

Statische Methoden und Felder gehören nicht zu Klasseninstanzen, sondern zu Klassendefinitionen. Statische Methoden spielen keine Rolle im virtuellen Dispatching-Mechanismus und sind nicht Teil der virtuellen Methodentabelle.

Sie sind nur Methoden und Felder für diese spezielle Klasse.

Es kann so aussehen, als ob die Methoden und Felder "geerbt" sind, weil Sie SpecificLogger.Log() ausführen können, aber das ist nur etwas, das Sie davon abhält, immer auf die Basisklasse zu verweisen.

Statische Methoden und Felder sind eigentlich nur globale Methoden und Felder, nur die OO-Art.

    
Pieter van Ginkel 16.11.2010 17:09
quelle
0

Sie überschreiben die Eigenschaft in der Basisklasse nicht, sondern verstecken sie stattdessen. Die tatsächliche Eigenschaft, die zur Laufzeit verwendet wird, hängt davon ab, für welche Schnittstelle Sie arbeiten. Das folgende Beispiel veranschaulicht:

%Vor%

In Ihrem Code arbeitet die Log-Methode tatsächlich gegen die BaseLogger-Schnittstelle - weil die Log-Methode Teil der BaseLogger-Klasse ist.

Statische Methoden und Eigenschaften können nicht überschrieben werden. Wenn Sie eine Eigenschaft ausblenden möchten, sollten Sie das Schlüsselwort new verwenden, um anzuzeigen, dass Sie etwas verstecken.

    
Simon Bartlett 16.11.2010 17:13
quelle
0

für meine Überraschung folgenden Code ist erlaubt und kompiliert ohne Fehler auf. NET Framework 4.5.1, VS 2013.

%Vor%     
Paras 08.09.2014 10:28
quelle

Tags und Links