2009-06-05 10 views
8

Conozco la función de esta palabra clave, pero me gustaría saber cómo funciona en un nivel inferior.¿Cómo funciona la palabra clave "como" internamente?

¿Cuál es más rápido? ¿Y siempre producen el mismo resultado? Si lo hacen, ¿por qué hay dos formas diferentes?

// Is there an overhead? An internal try catch? 
Class123 obj = someobject as Class123; 

if (Class123 != null) 
{ 
    //OK 
} 

o

Class123 obj = null; 

if (someobject is Class123) 
{ 
    obj = (Class123)someobject; 
} 
+1

Mirando las respuestas, creo que "es" es solo saber si algún objeto es un tipo, y ellos hacen algo de acción, y no solo un yeso. Ejemplo: if (obj es ClassManager) {Write ("Usted es un gerente")} –

Respuesta

16

No hay try-catch interno sucediendo cuando se utiliza la palabra clave as. La funcionalidad está integrada en el compilador/CLR, hasta donde yo sé, por lo que la verificación de tipo es implícita y automática.

regla simple:
Utilizar un molde directo cuando siempre contar con el objeto de tener un tipo conocido (y por lo tanto recibe un error útil si es por azar del tipo incorrecto). Utilice la palabra clave as cuando el objeto es siempre del tipo conocido.

El motivo de la existencia de la palabra clave as es puramente para la comodidad del programador (aunque tiene razón al sugerir que un try-catch sería más lento). Se podría aplicar manualmente usted mismo como tal, como usted señala:

var castObj = (obj is NewType) ? (NewType)obj : null; 

Esto pone de relieve el hecho de que el 'como' palabra clave es principalmente allí con el propósito de concisión.

Ahora, la diferencia de rendimiento entre los dos es probable que sea insignificante. La palabra clave as es probablemente un poco más lenta debido a la comprobación de tipo, pero es poco probable que afecte al código en la gran mayoría de las situaciones. Como ya se dijo, la optimización prematura nunca es una cosa sabia para hacer. Punto de referencia, si realmente lo desea, pero le aconsejaría usar cualquier método que sea más conveniente/apropiado para su situación, y no preocuparse por el rendimiento en absoluto (o más adelante si es absolutamente necesario).

+0

No había pensado en el nulo, lo cual es importante, pero cuando digo "try", si el objeto no era convertible (lo siento para mal inglés) –

+4

Seguramente "como" y un reparto se comportan igual en presencia de un nulo? ¿No debería ser "usar un lanzamiento directo cuando nunca espera que el objeto sea de otro tipo, use 'como' cuando el objeto pueda ser de otro tipo?" – Grokys

+1

El como podría ser un poco más lento pero en si (x es T) (T) x.Foo() la comprobación de tipo es necesaria dos veces, probablemente un poco más lento que x == nul. –

5

ya que puede ser más rápido porque solo necesita activar el tipo una vez mientras está + emitido, debe verificar el tipo dos veces.

+0

No estoy seguro de que esto sea correcto. La palabra clave "como" hace exactamente lo mismo, solo implícitamente, que yo sepa. – Noldorin

+0

Esta explicación también se proporciona en FxCop como una regla de rendimiento. Así que supongo que es correcto. – Scoregraphic

22

Según MSDN: as (C# Reference):

el operador as es como una operación de fundición. Sin embargo, si la conversión no es posible, se devuelve nulo en lugar de generar una excepción. Considere la siguiente expresión:

expression as type 

Es equivalente a la siguiente expresión, excepto que la expresión se evalúa sólo una vez.

expression is type ? (type)expression : (type)null 

La primera variante (como operando) ...

string str1 = strAsObject as string; 
if (str1 != null) 
{ 
    this.blabla(str1); 
} 

...compila a este código IL:

L_0009: ldloc.1 
L_000a: isinst string 
L_000f: stloc.2 
L_0010: ldloc.2 
L_0011: ldnull 
L_0012: ceq 
L_0014: stloc.s CS$4$0000 
L_0016: ldloc.s CS$4$0000 
L_0018: brtrue.s L_0024 
L_001a: nop 
L_001b: ldarg.0 
L_001c: ldloc.2 
L_001d: call instance void TestWinFormsApplication001.Form1::blabla(string) 
L_0022: nop 
L_0023: nop 

... y la segunda variante (es el operando + fundido) ...

if (strAsObject is string) 
{ 
    string str2 = (string) strAsObject; 
    this.blabla(str2); 
} 

... compila a este código IL:

L_0024: ldloc.1 
L_0025: isinst string 
L_002a: ldnull 
L_002b: cgt.un 
L_002d: ldc.i4.0 
L_002e: ceq 
L_0030: stloc.s CS$4$0000 
L_0032: ldloc.s CS$4$0000 
L_0034: brtrue.s L_0047 
L_0036: nop 
L_0037: ldloc.1 
L_0038: castclass string 
L_003d: stloc.3 
L_003e: ldarg.0 
L_003f: ldloc.3 
L_0040: call instance void TestWinFormsApplication001.Form1::blabla(string) 
L_0045: nop 
L_0046: nop 

... para que vea la única diferencia es el código adicional castclass en la línea L_0038.

9

Para configurar algunas cosas en claro:

Tipo de fundición se debe hacer cuando esté seguro de que el objeto es del tipo que está echando a. Puede ser nulo (en ese caso, se devolverá un valor nulo, a menos que sea un tipo de valor al que se va a convertir)

Cuando no esté seguro, se puede usar el operador "as". Cuando el objeto no se puede convertir, o el objeto es nulo, se devolverá nulo.

El "como" operador se traduce en un comunicado IL dedicado (isinst), mientras que un tipo de molde se traduce en la declaración IL castclass, por lo que se construye en el tiempo de ejecución. El compilador simplemente emite la declaración IL correcta.

+0

Sí, esto es cierto. Up-votado, ya que usted merece gran parte del crédito por las aclaraciones aquí. – Noldorin

+0

Hubiera editado su respuesta en lugar de crear una nueva, pero no estoy autorizado a hacerlo (todavía) :-) –

5

Esta pregunta ya ha sido bien respondida, sin embargo, hasta ahora ha faltado números duros.

Over 100000000 iterations 
AS : Failure 00:00:00.9282403 
Cast : Failure 00:00:00.9868966 
AS : Success 00:00:00.9350227 
Cast : Success 00:00:01.1382759 

Las cifras constantemente regresan en estas proporciones

Quiero señalar que la única conclusión a la toma de estas cifras es que desde el punto de vista del rendimiento, hay muy poco que ganar eligiendo uno de estos métodos sobre el otro. Hay muy poco en la diferencia para una sola llamada (donde muy poco tiende a cero). Dicho esto, "como" es más rápido :)

Después de eso, las cifras anteriores en su mayoría son razonables.

"Como" tarda más en fallar que en éxito. En caso de éxito no sucede nada, el valor puede usarse tal cual, o simplemente copiarse. En caso de falla, se requiere un salto para copiar una referencia nula.

"Emitir" es más rápido en el fracaso, una llamada a "es" y no hace más. En el caso del éxito es mucho más lento, tiene el encabezado de la llamada al "es" y luego al elenco.

Sin embargo me sorprende que en caso de fallo moldeada lleva más tiempo que como insuficiencia

Editar

Conforme a lo solicitado, las cifras de molde en un bloque try/catch

Over 100000000 iterations 
Catch : Failure 05.05:00:00 // approximately, because I didn't hang around 
Catch : Success 00:00:01.4000952 

El código que produjo el primer conjunto de figuras

class Program 
{ 
    const int ITERATION_COUNT = 100000000; 
    private static UInt64 stringCount = 0; 
    private static UInt64 objectCount = 0; 
    static void Main(string[] args) 
    { 
     Console.WriteLine("Over {0} iterations ", ITERATION_COUNT); 

     string s = "Hello"; 
     object o = new Int32(); 

     RunTest("AS : Failure {0}", TestAs, o); 
     RunTest("Cast : Failure {0}", TestIs_And_Cast, o); 
     RunTest("AS : Success {0}", TestAs, s); 
     RunTest("Cast : Success {0}", TestIs_And_Cast, s); 

     Console.WriteLine("Press any key to stop"); 
     Console.ReadKey(); 

    } 
    private static void RunTest(string testDescription, Action<object> testToRun, object arg) 
    { 
     Stopwatch sw = new Stopwatch(); 
     sw.Start(); 
     for (int i = 0; i < ITERATION_COUNT; i++) 
      testToRun(arg); 
     sw.Stop(); 
     Console.WriteLine(testDescription, sw.Elapsed); 
    } 
    static void TestAs(object obj) 
    { 
     string s = obj as string; 
     if (s != null) 
      stringCount++; 
     else 
      objectCount++; 
    } 
    static void TestIs_And_Cast(object obj) 
    { 
     string s = null; 
     if (obj is string) 
     { 
      s = (string)obj; 
      stringCount++; 
     } 
     else 
      objectCount++; 
    } 
} 
+2

"as" es más elegante que "is" seguido de un elenco. Estoy de acuerdo en que el impacto en el rendimiento es insignificante. Tal vez también puede agregar un caso de prueba utilizando el reparto con manejo de excepciones. Con suerte, eso hará que algunas personas se den cuenta de que la captura de excepciones para este tipo de cosas "no está hecha". :) –

+0

Entonces, ¿cuántos de estos (como o molde) se pueden hacer por milisegundo? Dado que hubo 100 millones de cheques en aproximadamente 1 segundo, ya sea para: -> 100,000,000/1000 = 100,000. Por lo tanto, se pueden hacer 100 mil comprobaciones o cast * por milisegundo *. O bien: cada control (de cualquiera, más o menos) lleva 100 millonésimas de segundo. –

Cuestiones relacionadas