web-dev-qa-db-fra.com

'is' versus try cast avec test nul

J'ai remarqué que Resharper suggère que je tourne ceci:

if (myObj.myProp is MyType)
{
   ...
}

dans ceci:

var myObjRef = myObj.myProp as MyType;
if (myObjRef != null)
{
   ...
}

Pourquoi suggérerait-il ce changement? J'ai l'habitude de Resharper suggérant des modifications d'optimisation et des modifications de réduction de code, mais cela donne l'impression de vouloir transformer mon unique déclaration en un doublon.

Selon MSDN :

Un est expression est évalué à true si les deux conditions suivantes sont remplies:

expression n'est pas nul. expression peut être convertie en type. C'est-à-dire une expression exprimée de la forme (type)(expression) se terminera sans lancer d’exception.

Est-ce que je comprends mal ou est-ce que is ne fait pas exactement les mêmes vérifications, sur une seule ligne, sans qu'il soit nécessaire de créer explicitement une autre variable locale pour le contrôle nul?

100
HotN

Parce qu'il n'y a qu'un casting. Comparez ceci:

if (myObj.myProp is MyType) // cast #1
{
    var myObjRef = (MyType)myObj.myProp; // needs to be cast a second time
                                         // before using it as a MyType
    ...
}

pour ça:

var myObjRef = myObj.myProp as MyType; // only one cast
if (myObjRef != null)
{
    // myObjRef is already MyType and doesn't need to be cast again
    ...
}

C # 7.0 prend en charge une syntaxe plus compacte utilisant correspondance de motif :

if (myObj.myProp is MyType myObjRef)
{
    ...
}
129
Jeff E

La meilleure option est d'utiliser un modèle de correspondance comme celui-ci:

if (value is MyType casted){
    //Code with casted as MyType
    //value is still the same
}
//Note: casted can be used outside (after) the 'if' scope, too
8
Francesco Cattoni

Il n'y a pas encore d'informations sur ce qui se passe réellement au-dessous de la ceinture. Jetez un oeil à cet exemple:

object o = "test";
if (o is string)
{
    var x = (string) o;
}

Cela se traduit par l'IL suivant:

IL_0000:  nop         
IL_0001:  ldstr       "test"
IL_0006:  stloc.0     // o
IL_0007:  ldloc.0     // o
IL_0008:  isinst      System.String
IL_000D:  ldnull      
IL_000E:  cgt.un      
IL_0010:  stloc.1     
IL_0011:  ldloc.1     
IL_0012:  brfalse.s   IL_001D
IL_0014:  nop         
IL_0015:  ldloc.0     // o
IL_0016:  castclass   System.String
IL_001B:  stloc.2     // x
IL_001C:  nop         
IL_001D:  ret   

Ce qui compte ici, ce sont les appels isinst et castclass, tous deux relativement coûteux. Si vous comparez cela à l’alternative, vous pouvez voir que c’est uniquement un contrôle isinst:

object o = "test";
var oAsString = o as string;
if (oAsString != null)
{

}

IL_0000:  nop         
IL_0001:  ldstr       "test"
IL_0006:  stloc.0     // o
IL_0007:  ldloc.0     // o
IL_0008:  isinst      System.String
IL_000D:  stloc.1     // oAsString
IL_000E:  ldloc.1     // oAsString
IL_000F:  ldnull      
IL_0010:  cgt.un      
IL_0012:  stloc.2     
IL_0013:  ldloc.2     
IL_0014:  brfalse.s   IL_0018
IL_0016:  nop         
IL_0017:  nop         
IL_0018:  ret  

Il convient également de mentionner qu'un type de valeur utilisera unbox.any plutôt que castclass:

object o = 5;
if (o is int)
{
    var x = (int)o;
}

IL_0000:  nop         
IL_0001:  ldc.i4.5    
IL_0002:  box         System.Int32
IL_0007:  stloc.0     // o
IL_0008:  ldloc.0     // o
IL_0009:  isinst      System.Int32
IL_000E:  ldnull      
IL_000F:  cgt.un      
IL_0011:  stloc.1     
IL_0012:  ldloc.1     
IL_0013:  brfalse.s   IL_001E
IL_0015:  nop         
IL_0016:  ldloc.0     // o
IL_0017:  unbox.any   System.Int32
IL_001C:  stloc.2     // x
IL_001D:  nop         
IL_001E:  ret   

Notez cependant que cela ne se traduit pas nécessairement par un résultat plus rapide, comme nous pouvons le voir ici . Il semble cependant y avoir eu des améliorations depuis que cette question a été posée: les conversions semblent être effectuées aussi rapidement qu’elles étaient, mais as et linq sont maintenant environ 3 fois plus rapides.

6
Jeroen Vannevel

Avertissement Resharper:

"Type check and direct cast can be replaced with try cast and check for null"

Les deux fonctionneront, cela dépend de la manière dont votre code vous convient le mieux. Dans mon cas, j'ignore simplement cet avertissement:

//1st way is n+1 times of casting
if (x is A) ((A)x).Run();
else if (x is B) ((B)x).Run();
else if (x is C) ((C)x).Run();
else if (x is D) ((D)x).Run();
//...
else if (x is N) ((N)x).Run();    
//...
else if (x is Z) ((Z)x).Run();

//2nd way is z times of casting
var a = x as Type A;
var b = x as Type B;
var c = x as Type C;
//..
var n = x as Type N;
//..
var z = x as Type Z;
if (a != null) a.Run();
elseif (b != null) b.Run();
elseif (c != null) c.Run();
...
elseif (n != null) n.Run();
...
elseif (x != null) x.Run();

Dans mon code, la 2ème manière est une performance plus longue et moins bonne.

4
Tom

Pour moi, cela semble dépendre de la probabilité que cela se produise ou non. Il serait certainement plus efficace de faire la distribution à l’avant si l’objet est de ce type la plupart du temps. Si ce n'est que de temps en temps, il peut être préférable de vérifier d'abord avec is.

Le coût de la création d'une variable locale est très négligeable par rapport au coût de la vérification de type.

La lisibilité et la portée sont généralement les facteurs les plus importants. Je ne suis pas d'accord avec ReSharper et j'utilise l'opérateur "is" pour cette seule raison; optimiser plus tard s'il s'agit d'un véritable goulot d'étranglement.

(Je suppose que vous utilisez uniquement myObj.myProp is MyType une fois dans cette fonction)

3
Derrick

Cela devrait aussi suggérer un deuxième changement:

(MyType)myObj.myProp

dans

myObjRef

Cela enregistre un accès à la propriété et une conversion, par rapport au code d'origine. Mais ce n'est possible qu'après avoir changé is en as.

0
Ben Voigt

Je dirais que cela consiste à créer une version fortement typée de myObj.myProp, qui est myObjRef. Cela devrait alors être utilisé lorsque vous référencez cette valeur dans le bloc, par opposition à une conversion.

Par exemple, ceci:

myObjRef.SomeProperty

c'est mieux que ça:

((MyType)myObj.myProp).SomeProperty
0
Jerad Rose