2016-08-02 1 views
3

J'écris de l'IL personnalisé et j'ai besoin de quelque chose d'équivalent à return SomeStaticField != null;. C'était ma conclusion naturelle:Pourquoi cette vérification null .NET IL ne fonctionne-t-elle pas comme prévu?

volatile.ldsfld ...SomeStaticField //volatile is needed here for unrelated reasons 
ldnull 
ceq 
not 
ret 

Cependant, cela ne semble pas fonctionner. J'ai confirmé que SomeStaticField est null, mais cette fonction finira par retourner vrai. Je sais que C# utilise les branches pour une telle construction, et je pourrais l'utiliser aussi, mais il me déconcerte pourquoi cela ne serait pas le comportement attendu

Un exemple complet et vérifiable (comme une bibliothèque):

.assembly extern /*23000001*/ mscorlib 
{ 
    .publickeytoken = (B7 7A 5C 56 19 34 E0 89)       // .z\V.4.. 
    .ver 4:0:0:0 
} 
.assembly 'BareMetal' 
{ 
    .custom instance void class [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::'.ctor'() = (
       01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 
       63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01  ) // ceptionThrows. 

    .hash algorithm 0x00008004 
    .ver 1:0:0:0 
} 
.module BareMetal.dll 


.namespace Earlz.BareMetal 
{ 
    .class public auto ansi abstract sealed beforefieldinit BareMetal 
     extends [mscorlib]System.Object 
    { 
    .method public static hidebysig 
      default bool FooTest() cil managed 
    { 
     .maxstack 2 
     ldnull 
     ldnull 
     ceq 
     not 
     ret 
    } 

    } 
} 
+0

Il devrait fonctionner, [CEQ est certainement utilisé pour les contrôles null] (http://stackoverflow.com/questions/24023705/when-i-use-is-operator-why-there-is-only -a-null-check-in-il-code). S'il vous plaît fournir un [mcve]. – Heinzi

+1

"pas" n'est-ce pas. Toujours préférable de laisser le compilateur C# générer le msil en premier et le regarder avec ildasm.exe. Personne ne pense à utiliser 'cgt.un'. –

+0

@Heinzi J'ai mis à jour pour ajouter un exemple complet (en remplaçant le ldsfld par un ldnull pour plus de simplicité) – Earlz

Répondre

6

not calcule le complément binaire de son opérande: ~1 est -2, ce qui, étant non nul, est vrai. La manière canonique de nier un booléen est ldc.i4.0 ; ceq. Et, comme @HansPassant l'a souligné, la manière canonique de faire une comparaison != null est directement cgt.un - toutes les références valides sont "supérieures à zéro" lorsqu'elles sont interprétées comme des valeurs non signées. Que de telles comparaisons sont en sécurité est explicitement documentée dans ECMA-335, section I.12.1.5:

En particulier, les références de l'objet peut être:

...

  1. Créé comme une référence null (ldnull)

...

pointeurs gérés avoir plusieurs opérations de base supplémentaires.

...

  1. comparaison et Unsigned branches conditionnelles basées sur deux pointeurs gérés (bge.un, bge.un.s, bgt.un, bgt.un.s, ble.un, ble.un.s, blt.un, blt.un.s, cgt.un, clt.un).
+0

J'ai toujours utilisé 'brtrue' ou' brfalse' pour faire une vérification nulle car je n'ai pas besoin de faire un 'ldnull' pour la comparaison. Y a-t-il une différence dans ce cas qui rend les versions de comparaison préférables? – Kyle

+1

@Kyle: non. C'est exactement ce que vous feriez si vous aviez besoin du résultat de la comparaison en tant que booléen, pour quelque raison que ce soit (peut-être pour combiner des expressions et enregistrer sur des branches plus tard).Si vous voulez vous brancher immédiatement, 'brtrue' et' brfalse' sont en effet la façon courante de le faire (si courante que 'brinst' et' brnull' sont des alias, en fait). Bien sûr, vous pouvez également ramifier et charger un booléen constant; ce qui est mieux dépend de ce que vous faites et de ce que le compilateur JIT génère finalement. –