c# .net casting resharper

c# - ''es'' versus intento de lanzamiento con cheque nulo



.net casting (7)

Aún no hay información sobre lo que sucede debajo del cinturón. Echale un vistazo a éste ejemplo:

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

Esto se traduce a la siguiente IL:

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

Lo que importa aquí son las llamadas isinst y castclass , ambas relativamente caras. Si compara eso con la alternativa puede ver que solo hace una verificación 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

También vale la pena mencionar que un tipo de valor usará unbox.any lugar de 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

Sin embargo, tenga en cuenta que esto no se traduce necesariamente en un resultado más rápido, como podemos ver here . Sin embargo, parece haber habido mejoras desde que se hizo esa pregunta: los moldes parecen realizarse tan rápido como solían ser, pero as y linq ahora son aproximadamente 3 veces más rápidos.

Noté que Resharper sugiere que cambie esto:

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

dentro de esto:

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

¿Por qué sugeriría este cambio? Estoy acostumbrado a Resharper, sugiriendo cambios de optimización y cambios de reducción de código, pero parece que quiere tomar mi único enunciado y convertirlo en un proyecto de dos líneas.

De acuerdo con MSDN :

Una expresión is es evaluada como verdadera si se cumplen las dos condiciones siguientes:

la expresión no es nula. la expresión se puede convertir a tipo . Es decir, una expresión moldeada de la forma (type)(expression) se completará sin lanzar una excepción.

¿Estoy malinterpretando eso, o no hago exactamente las mismas comprobaciones, solo en una línea sin la necesidad de crear explícitamente otra variable local para la verificación nula?


Advertencia de Resharper:

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

Ambos funcionarán, depende de cómo tu código te convenga más. En mi caso, simplemente ignoro esa advertencia:

//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();

En mi código, la segunda forma es más larga y peor rendimiento.


La mejor opción es usar coincidencia de patrones así:

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


Para mí, esto parece depender de cuáles sean las probabilidades de que sea de ese tipo o no. Ciertamente sería más eficiente lanzar el frente si el objeto es de ese tipo la mayor parte del tiempo. Si solo es ocasionalmente de ese tipo, puede ser más óptimo verificar primero con is.

El costo de crear una variable local es muy insignificante en comparación con el costo de la verificación de tipo.

La legibilidad y el alcance son los factores más importantes para mí, por lo general. No estoy de acuerdo con ReSharper y utilizo el operador "es" solo por esa razón; Optimice más tarde si esto es un verdadero cuello de botella.

(Supongo que solo está utilizando myObj.myProp is MyType una vez en esta función)


Porque solo hay un elenco. Compare esto:

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 ... }

a esto:

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 admite una sintaxis más compacta mediante la coincidencia de patrones :

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


También debería sugerir un segundo cambio:

(MyType)myObj.myProp

dentro

myObjRef

Esto ahorra un acceso a la propiedad y un reparto, en comparación con el código original. Pero solo es posible después de cambiar is as .


Yo diría que esto es para hacer una versión fuertemente tipada de myObj.myProp, que es myObjRef. Esto se debe usar cuando se hace referencia a este valor en el bloque, en lugar de tener que hacer un lanzamiento.

Por ejemplo, esto:

myObjRef.SomeProperty

es mejor que esto:

((MyType)myObj.myProp).SomeProperty