2011-01-10 3 views
11

Jak na przykład:Dlaczego C# nie pozwala mi używać tej samej nazwy zmiennej w różnych zakresach?

if (this.IsValid) 
{ 
    Matrix matrix = new Matrix(); 
} 

Matrix matrix = new Matrix(); 

kompilator ostrzega mnie mówiąc:

„lokalna zmienna o nazwie«matrix»nie może zostać uznane w tym zakresie, ponieważ dałoby to inny sens«matrix», który jest już używany w „dziecko” zakres oznaczający coś innego.

nie Czy te zmienne w różnych zakresach, więc nie będzie w stanie uzyskać dostęp do pierwszej matrix spoza if anyway?

+0

Nawet jeśli pytanie się kwalifikuje, zapobiega to nieostrożnym błędom. –

+0

tutaj [http://bytes.com/topic/c-sharp/answers/659303-already-used-child-scope-denote-something-else] możesz znaleźć wyjaśnienie od Jona Skeeta. I tutaj http://stackoverflow.com/questions/296755/child-scope-cs0136 także przez Jona Skeeta. –

+0

@Tim: Tak, ale za każdym razem trzeba tworzyć niepotrzebne nowe nazwy zmiennych, coś podobnego musi być użyte, aby zadowolić kompilator. IMO, jeśli kod jest jasny, nie spowoduje to żadnego problemu. Widzę, co każda macierz oznaczała bez robienia: matrixTemporary, matrixReal, itp. –

Odpowiedz

15

Odpowiedzi udzielone do tej pory są bardzo mylące. Prawidłowa analiza problemu rozpoczyna się od odczytywania komunikatu o błędzie. Komunikat o błędzie mówi ci, co faktycznie jest źle:

„lokalna zmienna o nazwie«matryca»nie może zostać uznane w tym zakresie, ponieważ dałoby to inny sens«matrycy», który jest już używany w ' Zakres dla oznaczenia czegoś innego:

Przeczytaj uważnie .Mówi ci dokładnie, która reguła C# jest naruszona, a mianowicie , że nie możesz używać tej samej nazwy do odniesienia się do dwóch różnych rzeczy w tym samym zakresie. (W rzeczywistości komunikat o błędzie jest nieco błędny, powinien powiedzieć "lokalny obszar deklaracji zmiennych", gdzie jest napisane "zakres", ale jest to dość rozwlekłe).

Ta reguła jest udokumentowana w specyfikacji C# 4.0, sekcja 7.6. 2.1: Proste nazwy, niezmienne znaczenie w blokach.

(Jest również nielegalne posiadanie dwóch zmiennych lokalnych o tej samej nazwie w nakładających przestrzenie deklaracji. Kompilator może być zgłoszenie tego błędu, jak również, ale zgłasza bardziej ogólny błąd w tym przypadku).

Czy te zmienne nie są w różnych zakresach, więc nie byłbym w stanie uzyskać dostępu do pierwszej macierzy poza instrukcją if?

Tak. To stwierdzenie jest nieistotne: , ale nie ma znaczenia. Błąd tutaj jest taki, że ta sama prosta nazwa została użyta w odniesieniu do dwóch różnych rzeczy w tej samej lokalnej przestrzeni deklaracji zmiennej.

Rozważmy następujący scenariusz:

class C 
{ 
    int x; 
    void M() 
    { 
     x = 10; // means "this.x" 
     for(whatever) 
     { 
      int x = whatever; 
     } 
    } 
} 

samą ofertę. Błąd tutaj polega na tym, że prosta nazwa "x" została użyta w zewnętrznym polu deklaracji, aby odwołać się do this.x, i została użyta w wewnętrznej przestrzeni deklaracji do określenia "zmiennej lokalnej". Używanie tej samej prostej nazwy do odwoływania się do dwóch różnych rzeczy w tym samym polu deklaracji - pamiętaj, że wewnętrzna przestrzeń deklaracji jest częścią części zewnętrznej - jest zarówno niebezpieczna, , jak i jest niezgodna z prawem.

Jest to mylące z oczywistych powodów; ma się uzasadnione oczekiwanie, że nazwa będzie oznaczać to samo wszędzie w przestrzeni deklaracji, w której jest używana po raz pierwszy. Jest to niebezpieczne, ponieważ niewielkie zmiany kodu są podatne na zmianę znaczenia:

class C 
{ 
    int x; 
    void M() 
    { 
     int x; 
     x = 10; // no longer means "this.x" 
     for(whatever) 
     { 
      x = whatever; 
     } 
    } 
} 

Jeśli przestrzenie deklarację, w której proste nazwy są najpierw używane są nie nakładających to jest legalne do prostych nazw odnosi się do różnych rzeczy:

class C 
{ 
    int x; 
    void M() 
    { 
     { 
      x = 10; // means "this.x" 
     } 
     for(whatever) 
     { 
      int x = whatever; // Legal; now the 
     } 
    } 
} 

Aby uzyskać więcej informacji, i zabawna historia o smażone jedzenie, zobacz

http://blogs.msdn.com/b/ericlippert/archive/tags/simple+names/

+0

@Eric: Dzięki Eric. Nie rozumiem, jak w twoim ostatnim przykładzie, druga int x jest poprawna. Czy to dlatego, że hermetyzowałeś x = 10 za pomocą {} s? –

+1

@ Joan: Poprawnie. Z x = 10 w swoim własnym bloku, teraz przestrzeń deklaracji zmiennej lokalnej, która ją otacza, nie * pokrywa się * z inną przestrzenią deklaracji zmiennych lokalnych, która zawiera inne użycie x. –

+0

@Eric: Dzięki Eric. Mam to teraz. Widziałem niektóre języki, gdy napisałem tak jak w pierwszym przykładzie, drugie x ukryłoby pierwsze, więc uważasz, że to zły projekt? Na koniec powiedziałeś w swoim komentarzu "... który zamyka się, że się nie nakłada ...", czy znasz miejsce, w którym mogę się dowiedzieć na temat nakładających się na siebie koncepcji w języku C#? Nie słyszałam tego wcześniej. –

7

Uważam, że robi się to, aby uniknąć niejasnych błędów lub kodu, który jest trudny do odczytania.

Stosując tę ​​samą nazwę zmiennej między zakresie sposobu i zakresu dziecko może prowadzić do kodu, który jest bardzo trudny do odczytania, ponieważ zmiennej typu i, co gorsza, co oznacza, można zmienić, a jedynie wskazówkę aby czytelnik być słowem kluczowym deklaracji typu przed zmienną.

Jednak mogę również powiedzieć, że IL wygenerowane dla metod przez kompilator C# będzie trzymać wszystkie deklaracje zmiennych na górze, więc może ten sterownik decyzji miał uprościć zmienne drzewo parsowania dla kompilatora.

W rzeczywistości, można znaleźć to na MSDN:

Zakres nazwy jest regionem tekstu programu, w którym jest możliwe w odniesieniu do podmiotu deklarowanej przez nazwę bez kwalifikacja nazwiska. Zakresy mogą być zagnieżdżone, a wewnętrzny zakres może oznaczać znaczenie nazwy od zewnętrznego zakresu. (Nie oznacza to usuwa jednak ograniczenia nałożone Section 3.3 że w zagnieżdżonej bloku nie jest możliwe zadeklarować zmienną lokalną w tym samym nazwą jak lokalna zmienna w bloku okalającego.) przeprowadzono nazwa z zakresu zewnętrznego jest następnie ukryta pod numerem w obszarze tekstu programu objętego przez wewnętrzny zakres, a dostęp do zewnętrznej nazwy jest możliwy tylko po kwalifikowaniu nazwy.

Podkreślenie dodane.

A z sekcji 3.3:

Każdy blok lub włączenia bloku tworzy inną przestrzeń deklaracji dla lokalnych zmiennych i stałych. Nazwy są wprowadzone do tej deklaracji przestrzeni poprzez deklaracje lokalnej zmiennej i lokalne-deklaracje stałe.Jeśli blok jest ciało konstruktora instancji , metodzie lub operator deklaracja, albo pobrać lub ustawić accessor dla deklaracji indeksatora, parametry zadeklarowane w takiej deklaracji są członkami bloku lokalnej deklaracji zmiennej przestrzeń. Lokalna przestrzeń deklaracji zmiennej bloku zawiera wszystkie zagnieżdżone bloki. Zatem w obrębie bloku zagnieżdżonego nie jest możliwe zadeklarowanie zmiennej lokalnej w bloku otaczającym.

Podkreślenie dodane.

Tak, chodzi o to, że podczas gdy zakresy są różne, zmienna przestrzeń jest taka sama.

2

Wyobraź sobie człowieka próbującego odczytać ten kod.

Z punktu widzenia innego programisty próbującego odczytać twój kod, czy widzisz, jak mylące byłoby posiadanie dwóch różnych zmiennych o tej samej nazwie? Nawet jeśli reprezentują to samo, trudno jest zająć się dwiema rzeczami o tej samej nazwie.

+0

Nie sądzę, że jest to mylące, ponieważ wyraźnie pokazuje, że matryca w środku jeśli ma być tymczasowy, można go utworzyć, aby wykonać pewne obliczenia itp. –

5

Zawsze można to zrobić ...

void YourMethod() 
{ 
    if (this.IsValid) 
    {  
     Matrix matrix = new Matrix(); 
    } 

    { 
     Matrix matrix = new Matrix(); 
    } 
} 

... Każdy zestaw szelki {} pozwala na gnieździe inny poziom zakresie. Problem, który masz, polega na tym, że zagnieżdżone zakresy obejmują zakres ich rodziców. Jeśli zadeklarujesz zakres siblng, będzie mógł ponownie użyć zmiennych w obrębie tego samego rodzica. Ale jak zauważyli inni, może to później stać się mylące.

+0

Dzięki, ale nie mogę. Druga matryca i następujące po niej instrukcje muszą zawsze być wykonywane (niezależnie od tego, czy to.IsValid czy nie). –

+1

Do downwizera. Dlaczego to jest złe? Możesz spróbować. Przekonasz się, że jesteś tym, który jest zły. Drugi zestaw nawiasów klamrowych deklaruje, że zakres dodatkowy jest zawsze wykonywany. To nie jedna z nich. –

+0

@Joan, wykona oba. –

1
Matrix matrix = new Matrix(); 

if (this.IsValid) 
{ 
    Matrix matrix = new Matrix(); 
} 

wyobrazić, że zamiast napisany tak, to jest nieco bardziej oczywiste, to myślę, dlaczego nie jest to dozwolone, ponieważ druga instancja należy oczywiście uznać za zderzenie. Brak możliwości uzyskania dostępu do zmiennych zewnętrznych w zakresach potomnych byłby zły.

Z MSDN "O: To jest prawidłowe zachowanie i jest opisane w sekcji 3.7 specyfikacji językowej." Zakres zmiennej lokalnej zadeklarowanej w deklaracji zmiennej lokalnej (8.5.1) jest blok, w którym występuje deklaracja "." ... "Takie zachowanie ma na celu zmniejszenie prawdopodobieństwa ponownego użycia niepoprawnych nazw zmiennych (takich jak wycięcie i wklejenie)". (http://blogs.msdn.com/b/csharpfaq/archive/2004/05/18/why-can-ti-use-the-same-variable-as-an-inner-loop-does.aspx)

+0

To wydaje się być bardziej niezrozumiałym IMO, ale nawet w takim przypadku oczekiwałbym, że kompilator ukryje zewnętrzną zmienną zasięgu i utworzy nową. Ale w moim przykładzie można by pomyśleć, że pierwsza macierz jest już w innym zakresie, więc nie byłaby w stanie wpływać na drugą zmienną matrycy, tak jak jej dostępność nie wykracza poza instrukcję if, która jest zawarta w niej. –