2010-07-27 22 views
17

Sintácticamente veo que giran indefinidamente hasta que se alcanza una declaración de interrupción, pero ¿se compilan para la misma cosa? ¿Es ligeramente más rápido porque no tiene una condición para verificar? Aparte de la legibilidad del código, ¿hay alguna diferencia?Diferencia entre for (;;) y while (true) en C#?

+0

Ver http://stackoverflow.com/questions/2288856/when-implementing-an-infinite-loop-is-there-a-difference-in-using-while1-vs-fo –

+0

Esta es una buena pregunta. Me parece interesante ver que el compilador optimiza el código en un bucle do while en el código de ejemplo con el que probé esto. –

+1

'for (;;)' es A) escrito por antiguos codificadores de C, y B) cuatro caracteres más cortos :) – hobbs

Respuesta

36

Dada esta entrada:

private static void ForLoop() 
{ 
    int n = 0; 
    for (; ;) 
    { 
     Console.WriteLine(n++); 
    } 
} 

private static void WhileLoop() 
{ 
    int n = 0; 
    while (true) 
    { 
     Console.WriteLine(n++); 
    } 
} 

... se obtiene este resultado:

.method private hidebysig static void ForLoop() cil managed 
{ 
    // Code size  14 (0xe) 
    .maxstack 3 
    .locals init ([0] int32 n) 
    IL_0000: ldc.i4.0 
    IL_0001: stloc.0 
    IL_0002: ldloc.0 
    IL_0003: dup 
    IL_0004: ldc.i4.1 
    IL_0005: add 
    IL_0006: stloc.0 
    IL_0007: call  void [mscorlib]System.Console::WriteLine(int32) 
    IL_000c: br.s  IL_0002 
} // end of method Program::ForLoop 


.method private hidebysig static void WhileLoop() cil managed 
{ 
    // Code size  14 (0xe) 
    .maxstack 3 
    .locals init ([0] int32 n) 
    IL_0000: ldc.i4.0 
    IL_0001: stloc.0 
    IL_0002: ldloc.0 
    IL_0003: dup 
    IL_0004: ldc.i4.1 
    IL_0005: add 
    IL_0006: stloc.0 
    IL_0007: call  void [mscorlib]System.Console::WriteLine(int32) 
    IL_000c: br.s  IL_0002 
} // end of method Program::WhileLoop 

notablemente similares, diría (idéntico, incluso).

+0

A menos que me falta algo, son * idénticos *, fuera de los comentarios y nombres de los identificadores. –

+0

@ T.E.D. sí, son idénticos. –

+18

Hmm, hay una diferencia en la línea 5 ... oh, espera, no, eso es un punto en mi monitor;) –

2

No he examinado el código de salida, pero no debería haber diferencia alguna. Cualquier compilador decente hará la optimización de bucle lo suficientemente simple para ver que la condición es una expresión constante, y por lo tanto no necesita comprobar cada iteración.

Si uno es más rápido que el otro, los escritores de compilador de C# necesitan algo 'splained a ellos ...

21

En los compiladores modernos, absolutamente nada.

Históricamente, sin embargo, for(;;) se implementó como un salto único, mientras que while(true) también tenía una verificación de verdadero.

Prefiero while(true), ya que deja más claro lo que estoy haciendo.

+2

+1 absolutamente. 'while (true)' es mucho más claro que 'for (;;)'. ¿Qué estoy esperando, de todos modos? –

+1

Estoy totalmente de acuerdo en que debes elegir el que creas que es más claro, aunque podría estar en desacuerdo sobre cuál es ... –

+2

¿por qué no 'loop: ... goto loop'? –

0

Recopilan lo mismo. Puede escribir una aplicación de prueba que implemente ambos métodos y luego use ILDASM para confirmar que IL es idéntico.

+3

O simplemente puede ver la respuesta de Fredrik y ahorrarse el problema. :) – DarLom

0

Un ensamblaje de depuración se cumple hasta while (verdadero). Usa el reflector y puedes ver los resultados.

static void Main(string[] args) 
    { 
     ExecuteWhile(); 

     ExecuteFor(); 
    } 

    private static void ExecuteFor() 
    { 
     for (; ;) 
     { 
      Console.WriteLine("for"); 
      string val = Console.ReadLine(); 
      if (string.IsNullOrEmpty(val)) 
      { 
       Console.WriteLine("Exit for."); 
       break; 
      } 
     } 
    } 

    private static void ExecuteWhile() 
    { 
     while (true) 
     { 
      Console.WriteLine("while"); 
      string val = Console.ReadLine(); 
      if (string.IsNullOrEmpty(val)) 
      { 
       Console.WriteLine("Exit while."); 
       break; 
      } 
     } 
    } 

Inspección del método ExecuteFor en Reflector.

private static void ExecuteFor() 
{ 
    while (true) 
    { 
     Console.WriteLine("for"); 
     if (string.IsNullOrEmpty(Console.ReadLine())) 
     { 
      Console.WriteLine("Exit for."); 
      return; 
     } 
    } 
} 

una versión optimizada del mismo código produce resultados diferentes para ExecuteFor

private static void ExecuteFor() 
{ 
    do 
    { 
     Console.WriteLine("for"); 
    } 
    while (!string.IsNullOrEmpty(Console.ReadLine())); 
    Console.WriteLine("Exit for."); 
} 

Por nivel de detalle aquí es el ExecuteWhile optimizado ...

private static void ExecuteWhile() 
{ 
    do 
    { 
     Console.WriteLine("while"); 
    } 
    while (!string.IsNullOrEmpty(Console.ReadLine())); 
    Console.WriteLine("Exit while."); 
} 
+0

¿Realmente se compila hasta 'while (true)', o el reflector lo descompila de esa manera? : p –

+0

Si observa el IL creado a partir de los mismos ensamblajes, lee de esa manera. –

0

Como han mencionado otros, con cualquier compilador moderno, no debería hacer absolutamente ninguna diferencia.

Hice una breve prueba en mi computadora, sincronizando un par de operaciones usando una u otra, y siempre tomaron casi el mismo tiempo. Por supuesto, debido a otros procesos en ejecución, estas pruebas no son 100% precisas, pero si hay una diferencia en la velocidad (no debería haber), entonces es una microscópica.

1

Si pudiera, sugiero que vea una pregunta algo diferente. Si usa cualquiera de estos con la frecuencia suficiente para importarle, probablemente esté estructurando su código de manera deficiente. Si bien hay cosas como los sistemas integrados que realmente funcionan para siempre, los bucles en la mayoría del código normal no lo hacen. Escribir un bucle que reclama para que funcione para siempre significa que ha ocultado la condición de salida para el bucle en algún lugar dentro, con algún otro flujo de control (p., if (whatever) break;) como real salir del bucle.

Eso puede y debe evitarse. Si bien hay situaciones en las que las declaraciones break tienen sentido, generalmente deben ser para manejar situaciones inusuales, no para escribir un ciclo que dice una cosa pero hace otra (es decir, dice "ejecutar para siempre", pero realmente "ejecutar hasta que se cumpla la condición")

+0

No puedo estar de acuerdo con esto. –

+0

Yo tampoco. Cuando estaba aprendiendo a programar por primera vez en PL/I (no sé si incluso tenía una declaración de "interrupción") el patrón era "lectura de línea de archivo; mientras que no es el marcador final, haz el ciclo, que termina leyendo la siguiente línea ". Eso parece una violación de "no te repitas". Sugeriría que leer la línea una vez, al comienzo del ciclo, y salir si es un marcador final, es un mejor estilo. – supercat

+0

@supercat: Estoy de acuerdo, pero no justifica nada. Debería estar haciendo 'while (file.getline())', not 'for (;;) if (! File.getline()) break;' –

Cuestiones relacionadas