Soweit ich weiß macht die Klasse sealed
in VTable nachsehen oder irre ich mich? Wenn ich eine Klasse sealed
mache, bedeutet das, dass alle virtuellen Methoden in der Klassenhierarchie auch mit sealed
?
Zum Beispiel:
%Vor%"Ich denke, ich kann das für die versiegelte Klasse privat machen"
Sie können den Zugriffsmodifizierer in der Vererbungshierarchie nicht ändern. Das heißt, wenn die Methode public
in der Basisklasse ist, können Sie sie in abgeleiteten Klassen nicht zu private
oder internal
oder protected
machen. Sie können den Modifikator nur ändern, wenn Sie Ihre Methode als new
:
Soweit ich weiß, macht das Versiegeln in VTable oder lieg ich falsch?
Die versiegelte Klasse ist die letzte in der Hierarchie, weil Sie sie nicht erben können. Die virtuelle Tabelle kann mit der Klasse sealed
verwendet werden, falls diese Klasse sealed
eine Methode von der Basisklasse überschreibt.
Wenn ich eine Klasse versiegelte, bedeutet das, dass alle virtuellen Methoden in Klassenhierarchie sind auch versiegelt markiert?
Sie können IL-Code sehen:
%Vor% Methode ist nicht als sealed
markiert. Auch wenn Sie diese Methode explizit als sealed
markieren, erhalten Sie den gleichen IL-Code.
Außerdem gibt es keinen Grund, die Methode als sealed
in sealed
class zu markieren. Wenn die Klasse sealed
ist, kannst du sie nicht erben, du kannst sie also nicht erben.
Über virtuelle Tabellen - Wenn die Methode überschrieben wird und Sie sie aus der virtuellen Tabelle löschen, können Sie sie niemals in der Vererbungshierarchie verwenden. Daher gibt es keinen Grund, die Methode zu überschreiben und nie in der Vererbungshierarchie zu verwenden.
Zunächst ist anzumerken, dass C # im Allgemeinen virtuelle Aufrufe an jede Instanzmethode für einen Referenztyp ausführt, auch wenn die Methode nicht virtuell ist. Dies liegt daran, dass es eine C # -Regel gibt, dass es illegal ist, eine Methode für eine NULL-Referenz aufzurufen, die keine .NET-Regel ist (in roher CIL, wenn Sie eine Methode für eine NULL-Referenz aufrufen und diese Methode nicht selbst auf ein Feld zugreift oder virtuelle Methode, es funktioniert gut) und die Verwendung von callvirt
ist eine billige Möglichkeit, um diese Regel durchzusetzen.
C # generiert call
anstelle von callvirt
für nicht virtuelle Instanzaufrufe in einigen Fällen, in denen es offensichtlich ist, dass die Referenz nicht null ist. Insbesondere mit obj?.SomeMethod()
, da die ?.
bedeutet, dass bereits ein Null-Check stattfindet, wird, wenn SomeMethod()
nicht virtuell ist, dies in call
kompiliert. Dies geschieht nur mit ?.
, da der Code zum Kompilieren von ?.
diese Überprüfung durchführen kann, während dies bei if (obj != null){obj.SomeMethod();}
nicht geschieht, weil der Code, der .
kompiliert, nicht weiß, dass er einer Nullprüfung folgt. Die involvierte Logik ist sehr lokalisiert .
Auf CIL-Ebene ist es möglich, eine virtuelle Tabellennachfrage für virtuelle Methoden zu überspringen. So funktioniert base
ruft an; kompiliert zu einem call
bei der Implementierung einer Methode statt einer callvirt
. Durch Erweiterung im Konstrukt obj?.SomeMethod()
, wobei SomeMethod
virtuell und versiegelt war (ob einzeln oder weil der Typ von obj
versiegelt wurde), wäre es theoretisch möglich, das als call
zur Implementierung des am weitesten abgeleiteten Typs zu kompilieren . Es gibt jedoch einige zusätzliche Überprüfungen, die insbesondere durchgeführt werden müssen, um sicherzustellen, dass sie immer noch korrekt funktionieren, wenn Klassen in der Hierarchie zwischen dem Deklarationstyp und dem versiegelten Typ Überschreibungen hinzufügen oder entfernen. Es würde einige globale Kenntnisse der Hierarchie erfordern (und die Gewissheit, dass sich das Wissen nicht ändert, was bedeutet, dass alle Typen, die sich in der Assembly befinden, gerade kompiliert wird), damit die Optimierung sicher ist. Und der Gewinn ist winzig. Und immer noch nicht die meiste Zeit aus dem gleichen Grund, dass callvirt
auch bei nicht-virtuellen Anrufen die meiste Zeit verwendet wird.
Ich glaube nicht, dass es irgendwo wo sealed
gibt, beeinflusst, wie der Compiler den Aufruf generiert, und es beeinflusst es sicherlich die meiste Zeit nicht.
Der Jitter ist zwar frei, mehr Wissen anzuwenden, aber auch hier ist der Unterschied gering. Ich würde Ihnen sicherlich empfehlen, Klassen zu markieren, die Sie nicht als sealed
überschreiben. Wenn der Jitter das nutzt, dann ist das großartig, aber der Hauptgrund, warum ich es empfehle, ist nicht die Leistung, sondern die Korrektheit. Wenn Sie irgendwo versuchen, eine Klasse zu überschreiben, die Sie sealed
markiert haben, dann entweder A. Sie haben gerade den Entwurf ein wenig geändert und wussten, dass Sie sealed
(.5seconds Arbeit entfernen) oder B entfernen müssen Du hast etwas an einem Ort getan, von dem du sicher warst, dass du es nicht in einem anderen tun würdest. Es ist gut, die kurze Pause des Nachdenkens zu haben.
Wenn ich eine Klasse versiegelte bedeutet dies, dass alle virtuellen Methoden in der Klassenhierarchie auch als versiegelt gekennzeichnet sind?
Sie gelten als versiegelt, wenn sie explizit als solche gekennzeichnet sind.
Sealed bedeutet, dass Sie nicht davon erben oder es überschreiben können. Wenn Sie nicht von Klasse B erben können, haben Sie keine Möglichkeit, die Methode M zu überschreiben. Was das Nachschlagen betrifft, denke ich nicht, dass du es speicherst. In Ihrem Beispiel müsste die Aufrufmethode O die Methode von Klasse A nachschlagen und aufrufen. Das geschieht über die vtable.
Wenn die Klasse oder Methode versiegelt wird, hat dies keinen Effekt, jeder Aufruf von B.M()
wird virtuell sein.
Wenn ich wetten müsste, warum das so ist, würde ich sagen, weil M()
in A
deklariert ist, überschreibt es nicht die Methode "gehören" zu B
.
Wenn Sie die IL
des generierten Codes überprüfen, sehen Sie, dass die entsprechende Anweisung callvirt instance string namespace.A::M()
ist und daher, selbst wenn B
versiegelt ist, der Aufruf virtuell sein muss.
Tags und Links c# inheritance oop vtable sealed