2008-09-17 5 views
10

En C# puede hacer un bloque dentro de un método que no está asociado a ninguna otra instrucción.¿Cuál es el valor de un bloque anónimo no asignado en C#?

public void TestMethod() 
    { 
     { 
      string x = "test"; 
      string y = x; 

      { 
       int z = 42; 
       int zz = z; 
      } 
     } 
    } 

Este código se compila y se ejecuta como si las llaves dentro del método principal no estuvieran allí. También note el bloque dentro de un bloque.

¿Hay alguna posibilidad de que esto sea valioso? Todavía no he encontrado ninguno, pero tengo curiosidad por conocer los hallazgos de otras personas.

Respuesta

11

Alcance y recolección de basura: cuando abandona el bloque no conectado, las variables declaradas en él quedan fuera del alcance. Eso permite que el recolector de basura limpie esos objetos.

Ray Hayes señala que el recolector de elementos no utilizados .NET no recogerá inmediatamente los objetos fuera del alcance, por lo que el alcance es el principal beneficio.

+1

¿Has verificado esto realmente en el nivel de VM? La mayoría de los compiladores y GC razonables tenderán a colapsar los dos bloques en uno solo durante el proceso de compilación y GC un valor después del último uso de la variable que hace referencia a él. – emk

+0

No he verificado la recolección de basura, pero he verificado el alcance. Estoy bastante seguro de que el recolector de basura .NET recogerá cosas que han dejado alcance/no están referenciadas. –

+2

¡El GC podría entrar en acción, pero puede que no lo haga por un tiempo! La pérdida de alcance no significa GC automático. Además, en este caso, para los tipos de valores no habría habido nada para GC. Scoping es el objetivo principal aquí, no GC. –

1

Por lo que puedo ver, solo sería útil desde el punto de vista de la organización. Realmente no puedo concebir ningún valor lógico al hacer eso. Quizás alguien tenga un ejemplo apropiado.

0

Una razón para hacer esto es que las variables 'z' y 'zz' no estarían disponibles para codificar debajo del final de ese bloque interno. Cuando haces esto en Java, la JVM empuja un marco de pila para el código interno, y esos valores pueden vivir en la pila. Cuando el código sale del bloque, el marco de pila se abre y esos valores desaparecen. Dependiendo de los tipos involucrados, esto puede evitar que tenga que usar la recolección de basura y/o montón.

3

Es un subproducto de una regla del analizador que es una declaración simple o un bloque. es decir, un bloque se puede usar donde quiera que una sola declaración pueda.

p. Ej.

if (someCondition) 
SimpleStatement(); 

if (SomeCondition) 
{ 
    BlockOfStatements(); 
} 

Otros han señalado que las declaraciones de variables están en el alcance hasta el final del bloque contenedor. Es bueno para vars temporales tener un alcance corto, pero nunca he tenido que usar un bloque por sí solo para limitar el alcance de una variable. Algunas veces usa un bloque debajo de una declaración de "uso" para eso.

Por lo general, no es valioso.

0

En C# - como c/C++/java - llaves indican un alcance. Esto dicta la vida de una variable. A medida que se alcanza el paréntesis de cierre, la variable queda inmediatamente disponible para una recolección de basura. En C++, haría que se llamara al destructor de una clase si la var representaba una instancia.

En cuanto al uso, el único uso posible es liberar un objeto grande pero tbh, establecerlo en nulo tendría el mismo efecto. Sospecho que el uso anterior probablemente sea solo para mantener a los programadores de C++ moviéndose al código administrado en un territorio familiar y cómodo. Si realmente desea llamar a un "destructor" en C#, normalmente implementa la interfaz IDisposable y utiliza el patrón "using (var) {...}".

Oisin

0

Incluso si era realmente útil por cualquier motivo (por ejemplo, control de alcance variable), me disuadirlo de tal constructo del punto de vista de la buena legibilidad de código antiguo.

0

No tiene ningún valor aparte de la semántica y para el alcance y la recolección de basura, ninguno de los cuales es significativo en este ejemplo limitado.Si crees que hace que el código sea más claro, para ti y para los demás, puedes usarlo. Sin embargo, la convención más aceptada para la clarificación semántica en el código general utilizaría saltos de línea sólo con la opción comentarios en línea:

public void TestMethod() 
{ 
    //do something with some strings 
    string x = "test"; 
    string y = x; 

    //do something else with some ints 
    int z = 42; 
    int zz = z; 
} 
4

Un ejemplo sería si quería volver a utilizar un nombre de variable, normalmente no se puede volver a utilizar nombres de las variables esto no es válida

 int a = 10; 
     Console.WriteLine(a); 

     int a = 20; 
     Console.WriteLine(a); 

pero esto es:

{ 
     int a = 10; 
     Console.WriteLine(a); 
    } 
    { 
     int a = 20; 
     Console.WriteLine(a); 
    } 

lo único que se me ocurre en este momento, es para exa Si estuvieras procesando un objeto grande, y extrajeras algo de información, y luego de eso vas a realizar un montón de operaciones, podrías poner el procesamiento de objetos grandes en un bloque, para que quede fuera del alcance, luego continuar con las otras operaciones

{ 
     //Process a large object and extract some data 
    } 
    //large object is out of scope here and will be garbage collected, 
    //you can now perform other operations with the extracted data that can take a long time, 
    //without holding the large object in memory 

    //do processing with extracted data 
1

Esto le permite crear un bloque de alcance en cualquier lugar. No es tan útil por sí mismo, pero puede hacer que la lógica más simple:

switch(value) 
{ 
    case const1: 
     int i = GetValueSomeHow(); 
     //do something 
     return i.ToString(); 

    case const2: 
     int i = GetADifferentValue(); 
     //this will throw an exception - i is already declared 
... 

En C# se puede utilizar una sección Cobertura de manera que los artículos declarados en cada caso sólo están en su alcance en ese caso:

switch(value) 
{ 
    case const1: 
    { 
     int i = GetValueSomeHow(); 
     //do something 
     return i.ToString(); 
    } 

    case const2: 
    { 
     int i = GetADifferentValue(); 
     //no exception now 
     return SomeFunctionOfInt(i); 
    } 
... 

Esto también puede funcionar para gotos y etiquetas, no es que a menudo los use en C#.

1

La razón práctica por la que existe es si desea restringir el alcance de alguna variable cuando no existe una necesidad imperiosa de introducir ningún otro motivo para el bloque. En la práctica real, esto prácticamente nunca es útil.

Personalmente, supongo que desde el punto de vista del lenguaje/compilador es más fácil decir que puede poner un bloque en cualquier lugar donde se espere una instrucción, y simplemente no se salieron de su camino para evitar que use sin una declaración if/for/method/etc.

Considere el comienzo this recent blog post de Eric Lippert. Una instrucción if no es seguida por una sola instrucción o una serie de declaraciones incluidas en llaves, simplemente es seguida por una sola instrucción. Cada vez que encierre entre 0 y N en llaves, usted hace que esa sección del código sea equivalente (desde el punto de vista del analizador del lenguaje) a una declaración. Esta misma práctica también se aplica a todas las estructuras de bucle, aunque, como explica el punto principal de la publicación del blog, no se aplica a los bloques try/catch/finally.

Al abordar los bloques desde ese punto de vista, la pregunta es: "¿Hay alguna razón de peso para evitar que los bloques se usen en cualquier lugar donde se pueda usar una única declaración?" y la respuesta es no".

Cuestiones relacionadas