8
votes

Que signifie "final" en IL?

Lorsque vous utilisez l'ildasm / ilasm, vous pouvez observer le code MSL / CIL produit par un compilateur (le compilateur C #, par exemple), et dans certains cas, vous pouvez voir qu'il existe des méthodes marquées comme finale virtuelle .

Qu'est-ce que final signifie dans ce contexte? Je suppose que cela signifie "Cette méthode ne peut pas être remplacée", alors même si cette méthode a une fente dans la table virtuelle, cette fente ne peut pas être écrasée par une méthode dans une classe dérivée.

Mais comment? Comment ça marche, dans la pratique?

est-il juste appliqué par le compilateur (par exemple, si vous essayez de remplacer une méthode scellée, elle échouera avec une erreur de compilation) ou par le CLR (par exemple, lorsque vous essayez la méthode de remplacement, le CLR se lancent), ou par les deux ? Y a-t-il un moyen légal de "supprimer", contournant, ce final , c'est-à-dire d'écrire une classe dérivée qui remplace cette méthode?

Mise à jour : J'ai probablement trouvé une façon (voir ma propre réponse) mais je ne suis pas sûr de tout cela est légal, soutenu, standard ... Donc j'ai posté une autre question sur ce nouveau (mais lié) problème ici

Je suis ouvert aux commentaires et, bien sûr, des moyens alternatifs (s'ils existent!) Pour le faire.


2 commentaires

Presque presque méchant la mise en œuvre finale, plus de chances de remplacer. Similaire à scellé en C #.


Il est destiné au vérificateur , la partie de la gigue qui valide le MSIL. Questions aux cours avec des méthodes virtuelles, ils sont très faciles à pirater. La mise en œuvre est simple, si quelqu'un ticks il et dérive de la classe pour remplacer la méthode, le vérificateur y crie et met un arrêt à celui-ci. La langue C # a été conçue pour ne jamais se tromper, mais que vous pourriez pirater en utilisant un faux assemblage de référence.


3 Réponses :


5
votes

Ceci est pour les méthodes provenant d'une interface. C'est un détail de mise en œuvre. Si une classe implémente une interface, la méthode de l'interface est marquée comme virtuel , c'est-à-dire pour le comportement polymorphique (une place dans la table virtuelle (V> (V>). Mais il est également marqué comme final (scellé) de sorte que les autres classes d'enfants implémentant la classe de base ne peuvent pas remplacer cette méthode particulière.

Considérez l'exemple suivant: xxx

de: CLR via C # (Jeffrey Richter)

Le compilateur C # nécessite une méthode qui implémente une interface La signature de méthode soit marquée comme publique. le CLR nécessite que La méthode d'interface soit marquée comme virtuelle. Si vous ne marquez pas explicitement la méthode comme virtuelle dans votre code source, le compilateur marque le méthode comme virtuelle et scellée; cela empêche une classe dérivée de remplacer la méthode d'interface. Si vous marquez explicitement la méthode comme virtuel, le compilateur marque la méthode comme virtuelle (et le laisse non scellé); Cela permet à une classe dérivée de remplacer l'interface Méthode.


3 commentaires

Il serait marqué comme final virtuel si vous implémentez explicitement la méthode d'interface également.


@HamleThakobyan, oui, il sera marqué comme final virtuel en cas de mise en oeuvre explicite également, tout le point est d'empêcher les classes dérivées de soméclass pour remplacer cette méthode particulière et atteindre polymorphisme en même temps


Merci pour votre réponse, mais je posais une chose légèrement différente: comment ça marche, dans la pratique?



3
votes

Que signifie final dans ce contexte? Je suppose que cela signifie "Cette méthode ne peut pas être remplacée", alors même si cette méthode a une fente dans la table virtuelle, cette fente ne peut pas être écrasée par une méthode dans une classe dérivée. p>

Oui. C'est aussi l'équivalent du mot-clé C # scellé code>, donc par exemple: p> xxx pré>

devient: p> xxx pré >

virtuel code> concerne le mot clé C # virtuel code> mais aussi à toute autre méthode virtuelle. Cela inclut remplacement code> ainsi que toute implémentation de l'interface. En particulier pendant que vous ne pouvez pas définir une méthode comme scellé virtuel code> en C # (car ce serait inutile, vous devriez donc décider de ce que vous voulez faire), vous pouvez en CIL, et cela se fait dans Quelques implémentations d'interface. P>

à la perspective C # Il existe trois façons de mettre en œuvre une méthode d'interface ou une propriété: p>

  1. non virtuel. LI>
  2. virtuel (donc les classes dérivées peuvent le remplacer). Li>
  3. Mise en œuvre explicite. LI> ol>

    à la perspective CIL Tout ce sont virtuel code> car ils utilisent tous le mécanisme de méthode virtuelle. Les premier et troisième sont également final code>. P>

    (considérez que nous pouvons ignorer le mécanisme de sur-conduit et utiliser appel code> en CIL pour appeler une méthode pour appeler une méthode Il est défini dans une classe, mais nous devons utiliser Callvirt code> avec une interface car nous ne savons pas quelle classe nous appelons, donc il doit y avoir une recherche). P>

    est-il juste appliqué par le compilateur (par exemple, si vous essayez de remplacer une méthode scellée, elle échouera avec une erreur de compilation) ou par le CLR (par exemple, lorsque vous essayez la méthode de remplacement, le CLR se lancent), ou par les deux ? p> blockquote>

    Les deux. p>

    Le compilateur C # ne vous permettra pas de remplacer une méthode scellée: p> xxx pré>

    Ceci refuse de compiler avec le compilateur Erreur CS0239: "'Test1.Tostring ()' ne peut pas remplacer les éléments hérités de l'élément" Test.Tostring () "car il est scellé". P>

    Mais si vous le forcez en écrivant le CIL vous-même: P > xxx pré>

    alors si vous appelez Nouveau test (). Tostring () code> Il renvoie "A" code> comme vous l'attendez, mais Si vous appelez nouveau test1 (). TOSTRING () CODE> Vous obtenez une erreur d'exécution: P>

    System.TypeLoadException : Declaration referenced in a method implementation cannot be a final method.  Type: 'TestAssembly.Test2'.  Assembly: 'TestAssembly, Version=1.0.0.1, Culture=neutral, PublicKeyToken=null'.
    


5 commentaires

Merci Jon, j'ai atteint les mêmes conclusions (j'écessais aussi avec elle); votre exemple et votre mien sont très similaires. Souhaitez-vous commenter mon dernier point cependant? Remplacer avec un nom différent?


Je veux dire votre seule option si vous avez vraiment besoin de remplacer est de faire une nouvelle version de la classe que vous souhaitez remplacer (ce qui est strictement un changement de rupture, bien que seuls de rares cas ne soient affectés négativement). Si ce n'est pas votre code, vous n'avez pas de chance.


Oh, attendez. Vous voulez dire explicitement dire que Y remplace X. Cela fonctionne, mais la chaîne de noms est différente pour la prochaine classe dérivée. Je suppose que cela pourrait appeler un nouveau x cependant, qui pourrait être remplacé à son tour.


Changer exactement le nom (:: x2 Remplacements :: x), disons qu'il remplace la méthode de la classe de base (base :: x) et la "finale" de la classe intermédiaire n'a plus d'importance. Ensuite, dans la même classe, définissez un neuf x qui appelle x2. Mais ça a l'air ... mal. Ou est-ce quelque chose de légal?


Je devrais probablement poster une question différente



1
votes

Je vais essayer de répondre à ma propre question avec quelques expériences que j'ai faites, car je voulais vraiment comprendre comment cela fonctionne dans la pratique em> (oui, même s'il s'agit d'un détail de mise en œuvre).

J'ai écrit une classe dans IL, nommée C2, qui étend une classe C # nommée C1. La méthode qu'elle remplace, M1, est écrite en IL comme finale virtuelle p> xxx pré>

ceci apparaît dans iLspy comme p> xxx pré>

Visual Studio et CSC le voient correctement comme M1 () et l'appelleront correctement (essayé dans le débogueur). P>

Maintenant, c'est ce que ma question était sur: cela devrait être la dernière méthode de la chaîne , mais qui dit cela? p>

J'ai écrit une classe dérivée C3, qui tente de remplacer la méthode à nouveau. p> xxx pré>

Il assemble avec ilasme, et produit une DLL. Dans ILSPY: P>

.class public auto ansi beforefieldinit N.C3
   extends N.C2
{ 
   .method private hidebysig virtual final 
      instance object  M1() cil managed
   {
      .override N.C1::M1


0 commentaires