2010-04-22 19 views
12
for (int i = 0; i < 10; i++) 
{ 
    Foo(); 
} 
int i = 10; // error, 'i' already exists 

----------------------------------------  

for (int i = 0; i < 10; i++) 
{ 
    Foo(); 
} 
i = 10; // error, 'i' doesn't exist 

Según mi comprensión del alcance, el primer ejemplo debería estar bien. El hecho de que ninguno de ellos esté permitido parece aún más extraño. Seguramente 'yo' está dentro del alcance o no.ámbito variable en los bloques de instrucciones

¿Hay algo no obvio en el alcance que no entiendo, lo que significa que el compilador realmente no puede resolver esto? ¿O es solo un caso de compilador de estado de niñera?

+0

Es como la ley del cinturón de seguridad. No hay ninguna razón técnica por la que no pueda conducir un automóvil sin una, pero es un buen trabajo que la ley exista para detenerlo, ya que puede salvarlo de algunas colisiones muy desagradables. –

Respuesta

16

Por mi comprensión del alcance, el primer ejemplo debe estar bien.

Su comprensión del alcance está bien. Esto no es un error de alcance. Es un uso inconsistente del error de nombre simple.

int i = 10; // error, 'i' ya existe

Ese no es el error que se informó. El error que se informó es "una variable local llamada i no puede ser declarada en este ámbito porque daría un significado diferente a lo que ya se utiliza en un ámbito secundario para denotar algo más"

El mensaje de error es diciéndole cuál es el error; leer el mensaje de error nuevamente No dice en ninguna parte que haya un conflicto entre las declaraciones; dice que el error es porque eso cambia el significado del nombre simple. El error es no la redeclaración; es perfectamente legal tener dos cosas en dos ámbitos diferentes que tengan el mismo nombre, incluso si esos ámbitos anidan. Lo que es no legal es tener un nombre simple significa dos cosas diferentes en declaraciones de variables locales anidadas espacios.

Se podría obtener el error "una variable local llamada i ya se ha definido en este ámbito" si en vez has hecho algo así como

int i = 10; 
int i = 10; 

Seguramente 'i' es ya sea en su alcance o no.

Seguro, pero ¿y qué? Si un i dado está en el alcance o no es irrelevante. Por ejemplo:

class C 
{ 
    int i; 
    void M() 
    { 
     string i; 

Perfectamente legal. El exterior i está en el alcance a lo largo de M. No hay ningún problema en absoluto al declarar un i local que sombrea el alcance externo. Lo que sería un problema es si usted dice

class C 
{ 
    int i; 
    void M() 
    { 
     int x = i; 
     foreach(char i in ... 

Porque ahora que usted ha utilizado i para significar dos cosas diferentes en dos espacios de declaración de variables locales anidados - una variable de bucle y un campo. Eso es confuso y propenso a errores, por lo que lo hacemos ilegal.

¿Hay algo no obvio en el alcance que no entiendo, lo que significa que el compilador realmente no puede resolver esto?

No entiendo la pregunta. Obviamente, el compilador es capaz de analizar completamente el programa; si el compilador no pudo resolver el significado de cada uso de i, ¿cómo podría informar el mensaje de error? El compilador puede determinar completamente que ha usado 'i' para significar dos cosas diferentes en el mismo espacio de declaración de variable local, e informa el error en consecuencia.

0

¿O solo es un caso de nanny-state compilacionismo?

Exactamente eso. No tiene sentido "reutilizar" nombres de variables en el mismo método. Es solo una fuente de errores y nada más.

+0

El problema no está en reutilizar nombres de variables; es perfectamente legal reutilizar un nombre de variable varias veces en un método. No es legal usar el mismo * nombre simple * para referirse a * dos cosas diferentes * dentro de un espacio de declaración de variable local particular. –

1

En el primer ejemplo, la declaración de i fuera del ciclo hace i una variable local de la función. Como resultado, es un error tener otro nombre de variable que haya declarado dentro de cualquier bloque de esa función.

El segundo, estoy en el alcance solo durante el ciclo. Fuera del ciclo, ya no se puede acceder.

Por lo que han visto los errores, pero no hay nada malo en hacer esto

for (int i = 0; i < 10; i++) 
{ 
    // do something 
} 

foreach (Foo foo in foos) 
{ 
    int i = 42; 
    // do something 
} 

Debido a que el alcance de i es limitada dentro de cada bloque.

9

Es porque el espacio de declaración define i en el nivel de método. La variable i está fuera del alcance al final del ciclo, pero aún no puede volver a declarar i, porque i ya estaba definido en ese método.

Ámbito vs Declaración Espacio:

http://csharpfeeds.com/post/11730/Whats_The_Difference_Part_Two_Scope_vs_Declaration_Space_vs_Lifetime.aspx

Usted querrá echar un vistazo a la respuesta de Eric Lippert (que por defecto es siempre la razón respecto a preguntas como las siguientes).

http://blogs.msdn.com/ericlippert/archive/2009/08/03/what-s-the-difference-part-two-scope-vs-declaration-space-vs-lifetime.aspx

Aquí es un comentario de Eric en el puesto mencionado anteriormente que creo que habla de por qué hicieron lo que hicieron:

a ver de esta manera. Siempre debe ser legal para mover la declaración de una variable UP en el código fuente tan largo como lo mantiene en el mismo bloque, ¿verdad? Si lo hicimos como lo sugiere , entonces eso a veces sería legal y, a veces, ¡ilegal! Pero lo que realmente queremos evitar es lo que sucede en C++ - en C++, a veces mover una declaración en realidad cambia las vinculaciones de otros nombres simples!

+1

Pero entonces ¿por qué se puede definir 'i' en el alcance de otro ciclo en el mismo método? Sigo pensando que es solo burocracia por parte del compilador. –

+1

@Michael: puede hacerlo porque los bucles son ámbitos hermanos. No hay colisión que defina variables con nombres idénticos en los ámbitos de hermanos porque están fuera del alcance de cada uno. –

+0

@Zach: Sin embargo, el primer bloque en la pregunta original muestra exactamente eso: No hay colisión. Pero aún no compilará. – Foxfire

0

mí piensa que el compilador quiere decir que i ha sido declarado en el nivel de método & con ámbito de dentro del bucle for.

Así, en el caso 1 - se obtiene un error que la variable ya existe, lo que lo hace

& en el caso 2 - ya que la variable está en el ámbito sólo dentro del bucle for, que no se puede acceder fuera de ese bucle

para evitar esto, se podía:

var i = 0; 

for(i = 0, i < 10, i++){ 
} 

i = 10; 

pero no puedo pensar en un caso en el que se quiere hacer esto.

HTH

0

que tiene que hacer

  int i ; 
      for (i = 0; i < 10; i++) 
      { 

      } 
      i = 10; 
+3

Ese es el código malvado. – Foxfire

1

Yea, el segundo "compilerism estado-niñera" comentario. Lo interesante es que esto está bien.

for (int i = 0; i < 10; i++) 
{ 

} 

for (int i = 0; i < 10; i++) 
{ 

} 

y esto está bien

for (int i = 0; i < 10; i++) 
{ 

} 

for (int j = 0; j < 10; j++) 
{ 
    var i = 12;     
} 

pero esto no es

for (int i = 0; i < 10; i++) 
{ 
    var x = 2; 
} 

var x = 5; 

a pesar de que usted puede hacer esto

for (int i = 0; i < 10; i++) 
{ 
    var k = 12; 
} 

for (int i = 0; i < 10; i++) 
{ 
    var k = 13; 
} 

Es todo un poco inconsistente.

EDITAR

Basado en el intercambio comentario con Eric abajo, pensé que podría ser útil para mostrar cómo intento de manipular bucles. Intento componer bucles en su propio método siempre que sea posible. Lo hago porque promueve la legibilidad.

ANTES

/* 
* doing two different things with the same name is unclear 
*/ 
for (var index = 0; index < people.Count; index++) 
{ 
    people[index].Email = null; 
} 
var index = GetIndexForSomethingElse(); 

DESPUÉS

/* 
* Now there is only one meaning for index in this scope 
*/ 
ClearEmailAddressesFor(people); // the method name works like a comment now 
var index = GetIndexForSomethingElse(); 

/* 
* Now index has a single meaning in the scope of this method. 
*/ 
private void ClearEmailAddressesFor(IList<Person> people) 
{ 
    for (var index = 0; index < people.Count; index++) 
    { 
     people[index].Email = null; 
    } 
} 
+1

No es inconsistente. La regla es que el mismo nombre simple no se puede usar para denotar dos cosas diferentes * en el espacio de declaración de variable local más exterior en el que el nombre simple se usa directamente *. Un ciclo for define un espacio de declaración de variable local. Creo que encontrará que esta regla se aplica constantemente en cada uno de sus ejemplos. –

+0

Puede ser consistente cuando analiza el lenguaje de la especificación, pero desde la perspectiva del consumidor es inconsistente. Si una variable está fuera del alcance y no se puede usar, entonces no hay una buena razón para no poder crear otra variable del mismo nombre. –

+0

Hay una * muy buena razón *. La muy buena razón es porque usar el mismo nombre simple para referirse a dos entidades diferentes en espacios de declaración superpuestos es * confuso * y * propenso a errores * y por lo tanto debe ser * ilegal *. –

5

Desde el C# spec on local variable declarations:

El alcance de una variable local declarada en una declaración-variable-locales es el bloque en el que se produce la declaración.

Ahora, por supuesto, no se puede utilizar iantes se declara, pero el alcance de la declaración i es el bloque toda que lo contiene:

{ 
    // scope starts here 
    for (int i = 0; i < 10; i++) 
    { 
     Foo(); 
    } 
    int i = 10; 
} 

La variable fori está en un ámbito secundario, de ahí la colisión de nombres variables.

Si reorganizamos la posición de la declaración, la colisión se hace más claro:

{ 
    int i = 10; 

    // collision with i 
    for (int i = 0; i < 10; i++) 
    { 
     Foo(); 
    } 
} 
+0

eso es una ilustración muy clara zach, gracias. – fearofawhackplanet

0
class Test 
{ 
    int i; 
    static int si=9; 

    public Test() 
    { 
     i = 199; 
    } 

    static void main() 
    { 
     for (int i = 0; i < 10; i++) 
     { 
      var x = 2; 
     } 

     { var x = 3; } 

     { // remove outer "{ }" will generate compile error 
      int si = 3; int i = 0; 

      Console.WriteLine(si); 
      Console.WriteLine(Test.si); 
      Console.WriteLine(i); 
      Console.WriteLine((new Test()).i); 
     } 
    } 
} 
Cuestiones relacionadas