Pourquoi «est» implémenté en tant que?

Étant donné que c’est un cas d’utilisation très naturel (si vous ne savez pas ce as fait réellement),

 if (x is Bar) { Bar y = x as Bar; something(); } 

est effectivement équivalent (c’est-à-dire que le CIL généré par le compilateur à partir du code ci-dessus sera équivalent) à:

 Bar y = x as Bar; if (y != null) { y = x as Bar; //The conversion is done twice! something(); } 

MODIFIER:

Je suppose que je n’avais pas précisé ma question. Je n’écrirais jamais le deuxième extrait, car il est bien entendu redondant. Je prétends que le CIL généré par le compilateur lors de la compilation du premier extrait est équivalent au deuxième extrait, qui est redondant. Questions: a) Est-ce correct? b) Si oui, pourquoi est is mis en œuvre comme ça?

C’est parce que je trouve le premier extrait beaucoup plus clair et plus joli que l’écrit bien écrit.

 Bar y = x as Bar; if (y != null) { something(); } 

CONCLUSION:

L’optimisation de l’ is / as cas n’est pas la responsabilité du compilateur, mais de l’ECE.

De plus, comme pour un contrôle nul, il a moins d’instructions (et moins chères) que les deux alternatives ( is et as et is et cast ).

Addenda:

CIL pour comme avec nullcheck (.NET 3.5):

 L_0001: ldarg.1 L_0002: isinst ssortingng L_0007: stloc.0 L_0008: ldloc.0 L_0009: ldnull L_000a: ceq L_000c: stloc.1 L_000d: ldloc.1 L_000e: brtrue.s L_0019 L_0011: ldarg.0 L_0019: ret 

CIL pour is et cast (.NET 3.5):

 L_0001: ldarg.1 L_0002: isinst ssortingng L_0007: ldnull L_0008: cgt.un L_000a: ldc.i4.0 L_000b: ceq L_000d: stloc.1 L_000e: ldloc.1 L_000f: brtrue.s L_0021 L_0012: ldarg.1 L_0013: castclass ssortingng L_0018: stloc.0 L_0019: ldarg.0 L_0021: ret 

CIL pour is et as (.NET 3.5):

 L_0001: ldarg.1 L_0002: isinst ssortingng L_0007: ldnull L_0008: cgt.un L_000a: ldc.i4.0 L_000b: ceq L_000d: stloc.1 L_000e: ldloc.1 L_000f: brtrue.s L_0021 L_0012: ldarg.1 L_0013: isinst ssortingng L_0018: stloc.0 L_0019: ldarg.0 L_0021: ret 

Celles-ci ont été éditées pour des raisons de concision (déclarations de méthodes, nops et appels à thing () supprimés).

a) est-ce correct

Oui, je l’aurais dit autrement. Vous dites que “est” est un sucre syntaxique pour le contrôle “suivi de”, suivi de “null”. Je l’aurais dit dans l’autre sens: ce “en tant que” est un sucre syntaxique pour “vérifier l’implémentation du type, cast en cas de succès, null en cas d’échec”.

C’est-à-dire que je serais plus enclin à dire

 if (x is Bar) { Bar y = x as Bar; something(); } 

est effectivement équivalent à

 if (x is Bar) { Bar y = (x is Bar) ? (Bar)x : (Bar) null; something(); } 

Vous voulez définir “comme” en termes de “est”, et non l’inverse. La question devrait vraiment être “pourquoi est-il mis en œuvre tel quel?” 🙂

b) Si oui, pourquoi est-il mis en œuvre comme ça?

Parce que c’est une implémentation correcte de la spécification .

Je pense que je ne suis pas votre pensée ici. Y at-il quelque chose de mal avec cette mise en œuvre? Comment préféreriez-vous qu’il soit mis en œuvre? Vous disposez des instructions “isinst” et “castclass”; décrivez le codegen de votre programme que vous aimeriez voir.

L’instruction IL disponible (isinst) renverra soit un object du type approprié, soit la valeur null si une telle conversion n’est pas possible. Et il ne jette pas une exception si la conversion n’est pas possible.

Etant donné que, “is” et “as” sont faciles à mettre en œuvre. Je ne prétends pas que “is” est implémenté en tant que “as” dans ce cas, mais que l’instruction IL sous-jacente permet aux deux de se produire. Maintenant, pourquoi le compilateur n’est pas capable d’optimiser le “est” suivi de “comme” en un seul appel, c’est un autre problème. Probablement, dans ce cas, il est lié à la scope variable (même si au moment où il s’agit de IL, la scope n’existe pas vraiment)

modifier

À bien y penser, vous ne pouvez pas optimiser “est” suivi de “comme” en un seul appel isinst, sans savoir que la variable en cours de discussion n’est pas sujette à la mise à jour à partir d’autres threads.

En supposant que x est une chaîne:

 //Thread1 if(x is ssortingng) //Thread2 x = new ComplexObject(); //Thread1 y = x as ssortingng 

Ici, y devrait être nul.

Dans votre exemple, l’utilisation de as est quand même redondante. Puisque vous savez déjà que x is Bar , vous devriez utiliser un cast:

 if (x is Bar) { Bay y = (Bar)x; } 

Sinon, convertissez en utilisant as et vérifiez simplement la valeur null:

 Bar y = x as Bar; if (y != null) { } 

Tout d’abord, je ne partage pas votre idée selon laquelle il s’agit d’un cas d’utilisation plus typique. C’est peut-être votre approche préférée, mais l’approche idiomatique est le style “as + null check”:

 Bar y = x as Bar; if (y != null) { something(); } 

Comme vous l’avez constaté, l’approche “est” nécessite un extra “en tant que” ou une dissortingbution, c’est pourquoi le “en tant que” avec contrôle nul est la méthode standard pour ce faire, selon mon expérience.

Je ne vois rien d’offensif à cette approche “en tant que”, personnellement, je ne trouve pas cela plus désagréable pour les yeux que pour tout autre code.

En ce qui concerne votre question, pourquoi le mot is clé is -il mis en œuvre en termes de mot-clé en as , je n’en ai aucune idée, mais j’aime bien le jeu de mots de votre question 🙂 l’outil (je pense que Reflector) que vous avez utilisé pour générer C # à partir de l’IL a interprété l’IL en termes de.

Vous ne ferez pas une seconde y = x as Bar; , parce que tu as déjà y qui est Bar.

Vous pouvez écrire le code maintenant

 DoIfOfType(possibleBar, b => b.something()) 

Je dirais que c’était un peu plus clair, mais pas aussi vite sans la vraie magie du compilateur.

D’après l’article de blog How Many Passes? par Eric Lippert qui est une passe de compilateur. Citer:

Ensuite, nous exécutons une passe d’optimisation qui réécrit les opérateurs sortingviaux “est” et “en tant que”.

C’est peut-être pour cette raison que vous voyez le même CIL généré pour les deux extraits.

La scope de ‘y’ est réduite si vous placez la déclaration dans la boucle.

Celui qui l’a écrit préfère probablement utiliser «x comme T» plus que «(T) x» et souhaite limiter la scope de «y».

Vous avez oublié les types de valeur. Par exemple:

  static void Main(ssortingng[] args) { ValueType vt; FooClass f = vt as FooClass; } private class FooClass { public int Bar { get; set; } } 

Ne comstackz pas car les types de valeur ne peuvent pas être convertis de la sorte.

Je soupçonne fortement que c’est plus rapide que et ne nécessite pas d’atsortingbution. Donc, si x est rarement jamais Bar, le premier extrait est bon. Si x est principalement Bar, alors ce qui est recommandé, car aucune deuxième conversion n’est requirejse. Cela dépend de l’utilisation et des circonstances du code.