Pourquoi le compilateur C # émet-il une instruction callvirt pour un appel de méthode GetType ()?

StackOverflow https://stackoverflow.com/questions/845657

Question

Je suis curieux de savoir pourquoi cela se produit. Veuillez lire l’exemple de code ci-dessous et l’IL correspondant émis dans les commentaires sous chaque section:

using System;

class Program
{
    static void Main()
    {
        Object o = new Object();
        o.GetType();

        // L_0001: newobj instance void [mscorlib]System.Object::.ctor()
        // L_0006: stloc.0 
        // L_0007: ldloc.0 
        // L_0008: callvirt instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()

        new Object().GetType();

        // L_000e: newobj instance void [mscorlib]System.Object::.ctor()
        // L_0013: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
    }
}

Pourquoi le compilateur a-t-il émis un callvirt pour la première section mais un call pour la deuxième? Y at-il une raison pour que le compilateur émette jamais une instruction <=> pour une méthode non virtuelle? Et s’il existe des cas dans lesquels le compilateur émettra un <=> pour une méthode non virtuelle, cela crée-t-il des problèmes pour la sécurité de type?

Était-ce utile?

La solution

Jouer en toute sécurité.

Techniquement, le compilateur C # n'utilise pas toujours callvirt

Pour les méthodes statiques & amp; méthodes définies sur les types de valeur, il utilise call. La majorité est fournie via l'instruction <=> IL.

La différence qui fait basculer le vote entre les deux réside dans le fait que <=> suppose que l'objet & "est utilisé pour passer l'appel &"; est non nulle. <=> vérifie par contre non nul et lève une exception NullReferenceException si nécessaire.

  • Pour les méthodes statiques, l'objet est un objet type et ne peut pas être null. Idem pour les types de valeur. Par conséquent, <=> est utilisé pour eux - de meilleures performances.
  • Pour les autres, les concepteurs de langage ont décidé d’utiliser <=> afin que le compilateur JIT vérifie que l’objet utilisé pour effectuer l’appel n’est pas nul. Même pour les méthodes d’instance non virtuelle, ils ont privilégié la sécurité au détriment des performances.

Voir aussi: Jeff Richter fait un meilleur travail à cet égard - dans son chapitre "Designing Types" dans CLR via C # 2nd Ed

Autres conseils

Voir ceci ancien article de blog d'Eric Gunnerson.

Voici le texte du message:

Pourquoi C # utilise-t-il toujours callvirt?

Cette question a été posée sur un alias C # interne et j’ai pensé que la réponse serait d’intérêt général. En supposant que la réponse soit correcte, cela fait un bon bout de temps.

Le langage .NET IL fournit à la fois une instruction call et une instruction callvirt, l’appel callt étant utilisé pour appeler des fonctions virtuelles. Mais si vous regardez à travers le code généré par C #, vous verrez qu'il génère un & "Callvirt &"; même dans les cas où aucune fonction virtuelle n'est impliquée. Pourquoi fait-il cela?

Je suis revenu sur mes notes de conception linguistique et elles indiquent clairement que nous avons décidé d'utiliser callvirt le 12/13/1999. Malheureusement, ils ne capturent pas notre raison de le faire, alors je vais devoir partir de ma mémoire.

Nous avions reçu un rapport de quelqu'un (probablement l'un des groupes .NET utilisant C # (je ne pensais pas qu'il s'appelait encore C # à ce moment-là)) qui avait écrit du code qui appelait une méthode sur un pointeur null. & # 8217; pas d'erreur car la méthode n'a pas & # 8217; pas d'accès aux champs (c'est-à-dire & # 8220; ce & # 8221; était nul, mais rien dans la méthode ne l'a utilisée) . Cette méthode a ensuite appelé une autre méthode qui utilisait ce point et lançait une exception, ce qui a provoqué un peu de casse-tête. Après avoir compris, ils nous ont envoyé une note à ce sujet.

Nous pensions que pouvoir appeler une méthode sur une instance nulle était un peu étrange. Peter Golde a fait quelques tests pour voir quel était l’impact positif de toujours utiliser callvirt, et il était suffisamment petit pour que nous décidions de faire le changement.

En tant que côté (peut-être) intéressant ... GetType() est inhabituel en ce qu'il n'est pas virtual - cela conduit à un certain très, très bizarre .

(marqué comme wiki car il est un peu hors sujet par rapport à la question)

Le compilateur ne connaît pas le type réel de o dans la première expression, mais il connaît le type réel dans la deuxième expression. On dirait qu’on ne regarde qu’une déclaration à la fois.

Cela convient, car C # dépend fortement du JIT pour son optimisation. Il est très probable que, dans un cas aussi simple, les deux appels deviendront des appels d'instance lors de l'exécution.

Je ne crois pas que callvirt soit jamais émis pour des méthodes non virtuelles, mais même si c'était le cas, cela ne poserait aucun problème, car la méthode ne serait jamais remplacée (pour des raisons évidentes).

Je suppose que c'est parce que la première attribue une variable, qui pourrait potentiellement contenir une instance d'un autre type rétablie qui aurait pu remplacer GetType (même si nous pouvons le voir non;); le second ne pourrait jamais être autre chose que Object.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top