2008-10-21 21 views
90

Dlaczego w instrukcji przełącznika C# dla zmiennej używanej w wielu przypadkach deklaruje się ją tylko w pierwszym przypadku?Deklaracja zmiennych w oświadczeniu przełącznika C#

Na przykład poniższy komunikat zgłasza błąd "Zmienna lokalna o nazwie" zmienna "jest już zdefiniowana w tym zakresie".

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

Jednak za logiką, wstępna deklaracja nie powinna być hit, jeśli typ jest Type.B. Czy wszystkie zmienne w instrukcji zmiany istnieją w jednym zakresie i czy są tworzone/przydzielane przed przetworzeniem jakiejkolwiek logiki?

+0

naprawdę brzydkie rzeczy jest to, że ludzie robią to: 'przełącznika (typ) {case Type.A: string variable = "x"; przerwa; case Type.B: variable = "y"; przerwa; } ' – giammin

+0

@giammin: Proszę dopracować. – zazkapulsk

+1

@zazkapulsk należy najpierw zadeklarować zmienną, a następnie użyć jej w przełączniku: 'string variable = null; switch (Type) {case Typ.A: variable = "x"; przerwa; case Type.B: variable = "y"; przerwa; } ' – giammin

Odpowiedz

39

Uważam, że ma to związek z ogólnym zakresem zmiennej, jest to zakres na poziomie bloku zdefiniowany na poziomie przełącznika.

Osobiście, jeśli ustawiasz wartość czegoś wewnątrz przełącznika w twoim przykładzie, aby naprawdę przynosił jakąkolwiek korzyść, mimo to chcesz ją zadeklarować poza przełącznikiem.

+35

Postępuj zgodnie z nawiasami klamrowymi. Zmienna istnieje tylko wewnątrz najbardziej wewnętrznych nawiasów klamrowych, w których pierwsza deklarowana jest zmienna. –

28

Tak, zakres jest całym blokiem przełącznika - niestety, IMO. Zawsze można dodać nawiasy klamrowe w jednym przypadku, aby utworzyć mniejszy zakres. Co do tego, czy są one tworzone/przydzielane - ramka stosu ma wystarczająco dużo miejsca dla wszystkich zmiennych lokalnych w metodzie (pomijając złożoność przechwyconych zmiennych). To nie jest tak, że przestrzeń jest przydzielana podczas wykonywania metody.

+3

Z całym szacunkiem, swoim Skeetness, nie zalecamy dodawania zakresu do bloku obudowy przełącznika. Jeśli potrzebujesz nowego zakresu dla tego bloku, szanse na to, że robisz za dużo w tym bloku. Zamiast tego proponuję polecić przetwarzanie zostać przekazane do wywołania funkcji. – Randolpho

+21

@Randolpho: Myślę, że to zbyt ogólne stwierdzenie. Może to być po prostu trzy lub cztery linie, ale wpływające na dwie lub trzy zmienne lokalne - wystarczające, aby ból był refaktorem dla wywołania metody. –

+4

Zgadzam się, natknąłem się na tę kwestię więcej niż jeden raz i było to dla logiki przetwarzania dwóch lub trzech linii ... Użycie tego w osobnej metodzie to po prostu ból. – Philippe

9

Ponieważ ich zakres znajduje się w bloku przełączającym. W dokumencie C# Language Specification podano:

Zakres zmiennej lokalnej lub stałej zadeklarowanej w bloku przełącznika jest blokiem przełączającym.

0

Inicjalizacja odbywa się w tej sprawie, ale deklaracja jest skutecznie wykonywana w górnej części zakresu. (Psuedo-code)

switch (Type) 
{ 
string variable; 

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

Jestem drobny pewien, że ten kod nie działa –

+3

@ Jim, tak, zdaję sobie sprawę, że kod nie działa - i dlatego odniosłem się do niego jako "Pseudo-Code", ale tak właśnie jest " skutecznie "zrobione. –

+0

Niezupełnie. Weź pod uwagę, że jeśli usuniesz 'break's, otrzymasz coś takiego: ' '' zmienna łańcuchowa = "x"; zmienna = "y"; '' ' To jest proceduralna' GOTO: 'dla wszystkich intencji i celów. Poprawny kod powinien być podobny do: '' ' string myVar; switch (myValue) { case MyEnum.A: myVar = "x"; przerwa; case MyEnum.B: myVar = "Y"; przerwa; } '' ' – percebus

0

Zmienne podzielają zakres w C# kompilator. Jednak zakres nie istnieje w taki sam sposób w CIL. Jeśli chodzi o faktyczne tworzenie/inicjowanie ... model pamięci .NET umożliwia ruch kompilatora czyta/zapisuje bit, o ile przestrzegane są proste reguły, chyba że zmienna jest oznaczona jako volatile.

168

Jeśli chcesz zmienna zawężona do konkretnego przypadku, po prostu zamknąć sprawę we własnym bloku:

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

Uwaga: nie zadziała, jeśli klamra zawiera tylko pierwszy blok, ale nie drugi. W takim przypadku druga "zmienna" nadal wydaje błąd, chociaż zakresy są różne. Wygląda jak błąd. –

+2

@ Hi-Angel: to nie jest błąd. Wiele zmiennych lokalnych o tej samej nazwie nie może być zadeklarowanych wewnątrz bloku lub jego zagnieżdżonych bloków. W efekcie blok zagnieżdżony "zawiera" zmienne lokalne zadeklarowane w zamkniętym bloku - nawet jeśli deklaracja występuje leksykalnie później w pliku. Dotyczy to również konstrukcji innych niż instrukcja switch. Zobacz 3.3 specyfikacji języka C# –

+0

Myślę, że dostałem to: jeśli zmienna istnieje w bloku, to nie mogę już używać tej samej nazwy w zagnieżdżonym bloku, nawet jeśli zagnieżdżony blok znajduje się wyżej od deklaracji zmiennej. Jest to nieracjonalne i powinno być błędem, ale błąd wyryty w dokumencie staje się funkcją ☺ –

-1

„W mojej Daaaaays ...”

swicth jest naprawdę prymitywne implementacja proceduralna, która istnieje już od czasów C (nawet przed C++).

Całość switch jest blok który służy jako zakres zamkniętym GOTO: (stąd : każdego case). Jeśli wziąłeś jakieś klasy asemblera, może się to wydawać znajome.

Dlatego switch użycie jest bardzo pomocne podczas łączenia z Enum s, a nie za pomocą break w każdy case jak

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; 
}