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?
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)
{
...
}
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
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.
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.
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)
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
.
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