2008-10-21 16 views
90

¿Por qué en una instrucción de cambio C#, para una variable utilizada en varios casos, solo la declara en el primer caso?Declaración de variables en una instrucción de cambio C#

Por ejemplo, lo siguiente arroja el error "Una variable local llamada 'variable' ya está definida en este ámbito".

switch (Type) 
{ 
    case Type.A: 
      string variable = "x"; 
       break; 
    case Type.B: 
      string variable = "y"; 
       break; 
} 

Sin embargo, por la lógica, la declaración inicial no debe ser golpeado si el tipo es Type.B. ¿Existen todas las variables dentro de una instrucción switch en un único ámbito, y se crean/asignan antes de procesar cualquier lógica?

+0

las cosas realmente feas es que la gente hace esto: 'interruptor (Tipo) {case Tipo.A: cadena variable = "x"; descanso; case Type.B: variable = "y"; descanso; } ' – giammin

+0

@giammin: Por favor, elabore. – zazkapulsk

+1

@zazkapulsk primero debe declarar una variable y luego usarla en el interruptor: 'string variable = null; switch (Tipo) {case Type.A: variable = "x"; descanso; case Type.B: variable = "y"; descanso; } ' – giammin

Respuesta

39

Creo que tiene que ver con el alcance general de la variable, es un ámbito de nivel de bloque que se define en el nivel de cambio.

Personalmente si establece un valor a algo dentro de un interruptor en su ejemplo para que realmente sea de alguna utilidad, querrá declararlo fuera del conmutador de todos modos.

+35

Sigue las llaves. Una variable solo existe dentro de las llaves más internas en las que la variable se declara por primera vez. –

28

Sí, el alcance es el bloque completo del interruptor - lamentablemente, IMO. Sin embargo, siempre puede agregar llaves en una sola caja para crear un alcance más pequeño. En cuanto a si se crearon/asignaron, el marco de pila tiene suficiente espacio para todas las variables locales en un método (dejando de lado las complejidades de las variables capturadas). No es como que el espacio se asigna durante la ejecución del método.

+3

Con todo el respeto debido a su astucia, no recomiende agregar alcance a un bloque de caja de interruptor. Si necesita un nuevo alcance para ese bloque, lo más probable es que esté haciendo demasiado en el bloque. En su lugar, sugiero que recomiende que el procesamiento sea llevado a una llamada de función. – Randolpho

+21

@Randolpho: Creo que es una afirmación demasiado general. Podría ser simplemente tres o cuatro líneas, pero afectando a dos o tres variables locales, lo suficiente como para que sea un esfuerzo refactorizar en una llamada a un método. –

+4

De acuerdo, me topé con este problema más de una vez, y fue por una lógica de procesamiento de dos o tres líneas ... Poner eso en un método separado es solo dolor. – Philippe

9

Porque su alcance está en el bloque de conmutadores. El C# Language Specification indica lo siguiente:

El alcance de una variable local o constante declarada en un bloque de conmutadores es el bloque de conmutadores.

0

La inicialización tiene lugar en el caso, pero la declaración se realiza efectivamente en la parte superior del alcance. (Pseudo-código)

switch (Type) 
{ 
string variable; 

    case Type.A: 
      variable = "x"; 
       break; 
    case Type.B: 
      variable = "y"; 
       break; 
} 
+12

Estoy seguro de que este código no funciona –

+3

@Jim, sí, soy consciente de que el código no funciona, por lo que me referí a él como "Pseudo-Code", pero es lo que es " efectivamente "hecho". –

+0

No del todo. Considere que si elimina el 'break's termina con algo como: ' '' string variable = "x"; variable = "y"; '' ' Este es un' GOTO 'de procedimiento para todos los propósitos. El código correcto debería ser algo así como '' ' string myVar; switch (myValue) { case MyEnum.A: myVar = "x"; descanso; case MyEnum.B: myVar = "Y"; descanso; } '' ' – percebus

0

Las variables comparten alcance en el compilador de C#. Sin embargo, el alcance no existe de la misma manera en CIL. En cuanto a la creación/inicialización real ... el modelo de memoria .NET permite que el compilador mueva lecturas/escrituras un poco, siempre y cuando se sigan reglas simples a menos que la variable esté marcada como volatile.

168

Si desea una variable con ámbito a un caso particular, sólo tiene que incluir el caso en su propio bloque:

switch (Type) 
{ 
    case Type.A: 
    { 
     string variable = "x"; 
     /* Do other stuff with variable */ 
    } 
    break; 

    case Type.B: 
    { 
     string variable = "y"; 
     /* Do other stuff with variable */ 
    } 
    break; 
} 
+2

Una nota: esto no funcionará si la llave incluye solo el primer bloque, pero no el segundo. En tal caso, la segunda «variable» sigue emitiendo el error, aunque los ámbitos son diferentes. Parece un error. –

+2

@ Hi-Angel: eso no es un error. No se pueden declarar múltiples variables locales con el mismo nombre dentro de un bloque o sus bloques anidados. En efecto, un bloque anidado 'contiene' variables locales que se declaran en el bloque adjunto, incluso si la declaración ocurre léxicamente más adelante en el archivo. Esto también es cierto para las construcciones que no sean una declaración de cambio. Ver 3.3 de la especificación de lenguaje C# –

+0

Creo que lo tengo: si la variable existe en el bloque, no puedo usar más el mismo nombre en un bloque anidado, incluso si el bloque anidado reside más alto de la declaración de la variable. Es irracional, y debería ser un error, pero un error grabado en un documento se convierte en una característica ☺ –

-1

"In My Daaaaays ..."

swicth es un muy primitiva implementación de procedimientos que ha existido desde las edades de C (incluso antes de C++).

El conjunto switch es un bloque deque sirve como un alcance-contenida GOTO: (de ahí el : en cada case). Si tomó algunas clases de ensamblador, eso podría parecerle familiar.

Por eso switch uso es más útil cuando se combina con Enum s y no usar break en cada case como

switch(mood) 
{ 
    case Mood.BORED: 
    case Mood.HAPPY: 
     drink(oBeer) // will drink if bored OR happy 
break; 

    case Mood.SAD: // unnecessary but proofs a concept 
    default: 
     drink(oCoffee) 
break; 
} 
Cuestiones relacionadas