Wie wird die virtuelle Tabelle los? Versiegelte Klasse

9

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 ?

gekennzeichnet sind

Zum Beispiel:

%Vor%     
Candid Moon 28.02.2017, 10:09
quelle

4 Antworten

2
  

"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 :

deklarieren %Vor%
  

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.

    
Roma Doskoch 28.02.2017, 10:14
quelle
2

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.

    
Jon Hanna 28.02.2017 11:34
quelle
0

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.

    
Palle Due 28.02.2017 10:18
quelle
0

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.

    
InBetween 28.02.2017 10:50
quelle

Tags und Links