remarks example cref c# .net as-keyword

example - remarks c#



¿Por qué se ''implementa'' como ''como''? (10)

Dado que este es un caso de uso muy natural (si no sabes lo as realmente hace),

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

es efectivamente equivalente (es decir, el CIL generado por el compilador del código anterior será equivalente) a:

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

EDITAR:

Supongo que no había dejado clara mi pregunta. Nunca escribiría el segundo fragmento ya que, por supuesto, es redundante. Afirmo que el CIL generado por el compilador al compilar el primer fragmento de código es equivalente al segundo fragmento, que es redundante. Preguntas: a) ¿Es esto correcto? b) Si es así, ¿por qué is implementa así?

Esto se debe a que el primer fragmento es mucho más claro y bonito que el que está realmente bien escrito.

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

CONCLUSIÓN:

Optimizar el caso es / no es responsabilidad del compilador, sino de los JIT.

Además, al igual que con un chequeo nulo, tiene menos instrucciones (y menos costosas) que las dos alternativas ( is and as and is y cast ).

Apéndice:

CIL para como con nullcheck (.NET 3.5):

L_0001: ldarg.1 L_0002: isinst string 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 para is y cast (.NET 3.5):

L_0001: ldarg.1 L_0002: isinst string 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 string L_0018: stloc.0 L_0019: ldarg.0 L_0021: ret

CIL para es y como (.NET 3.5):

L_0001: ldarg.1 L_0002: isinst string 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 string L_0018: stloc.0 L_0019: ldarg.0 L_0021: ret

Estos se han editado por falta (declaraciones de métodos, nops y llamadas a algo () eliminadas).


a) es esto correcto

Sí, aunque lo hubiera dicho de otra manera. Usted está diciendo que "es" es un azúcar sintáctico para la comprobación seguida por nula. Lo habría dicho de otra manera: que "as" es un azúcar sintáctico para "verificar la implementación de tipos, emitir si es correcto, nulo si falla".

Es decir, estaría más inclinado a decir

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

es efectivamente equivalente a

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

Vea, usted quiere definir "como" en términos de "es", no al revés. La pregunta realmente debería ser "¿por qué se implementa como está?" :-)

b) Si es así, ¿por qué se implementa así?

Porque eso es una correcta implementación de la especificación .

Creo que no estoy siguiendo tu línea de pensamiento aquí. ¿Hay algo mal con esa implementación? ¿Cómo preferirías que fuera implementado? Tienes a tu disposición las instrucciones "isinst" y "castclass"; describe el código para tu programa que te gustaría ver.


Bueno, la instrucción IL que está disponible (isinst) devolverá un objeto del tipo apropiado, o nulo si tal conversión no es posible. Y no lanza una excepción si la conversión no es posible.

Dado esto, tanto "es" como "como" son triviales de implementar. No diría que "es" se implementa como "como" en este caso, solo que la instrucción de IL subyacente permite que ambos ocurran. Ahora, por qué el compilador no puede optimizar el "es" seguido de "como" en una sola llamada isinst, eso es otro asunto. Probablemente, en este caso, está relacionado con el alcance de la variable (aunque en el momento en que este sea IL, el alcance no existe realmente)

Editar

Pensándolo bien, no puede optimizar "es" seguido de "como" en una sola llamada isinst, sin saber que la variable en discusión no está sujeta a actualización desde otros subprocesos.

Suponiendo que x es una cadena:

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

Aquí, y debería ser nulo.


El alcance de ''y'' se reduce si coloca la declaración dentro del bucle.

Quien lo haya escrito probablemente prefiera lanzar ''x como T'' más que ''(T) x'', y quería limitar el alcance de ''y''.


En primer lugar, no estoy de acuerdo con su premisa de que este es un caso de uso más típico. Puede ser su enfoque favorito, pero el enfoque idiomático es el estilo "como + comprobación nula":

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

Como ha encontrado, el enfoque "es" requiere el extra "como" o un reparto, por lo que, según mi experiencia, el "como" con comprobación nula es la forma estándar de hacerlo.

No veo nada ofensivo en este enfoque "as", personalmente no creo que sea más desagradable a la vista que cualquier otro código.

En cuanto a su pregunta real, ¿por qué se implementa la palabra clave en términos de la palabra clave as No tengo idea, pero me gusta el juego de palabras en su pregunta :) Sospecho que ninguna de las dos se implementa en términos de la otra, pero La herramienta (supongo que Reflector) que usaste para generar C # a partir de la IL interpretó la IL en términos de as .


En su ejemplo, el uso de as es redundante de todos modos. Como ya sabes que x is Bar , deberías usar un molde:

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

Alternativamente, convierte usando as y simplemente comprueba si hay un valor nulo:

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


No harás un segundo y = x as Bar; , porque tu ya tienes y que es bar.


Podrías escribir el código ahora como

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

Yo diría que fue un poco más claro, pero no tan rápido sin magia real del compilador.


Según la entrada del blog ¿Cuántos pases? por Eric Lippert que es un pase de compilación. Citar:

Luego ejecutamos un pase de optimización que reescribe trivial "es" y "como" operadores.

Entonces tal vez es por eso que estás viendo el mismo CIL generado para ambos fragmentos de código.


Sospecho fuertemente que es más rápido que como y no requiere una asignación. Entonces, si x es rara vez Bar, entonces el primer fragmento es bueno. Si x es mayormente Barra, entonces se recomendaría una, ya que no se requiere un segundo lanzamiento. Depende del uso y circunstancias del código.


Te olvidaste de los tipos de valor. P.ej:

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

No se compilará, ya que los tipos de valor no se pueden convertir de esta manera.