Zmienna zadeklarowana w for-loop is local variable?

Używam C# dość długo, ale nigdy nie zdałem sobie sprawy z tego, co następuje:

 public static void Main()
 {
     for (int i = 0; i < 5; i++)
     {

     }

     int i = 4;  //cannot declare as 'i' is declared in child scope                
     int A = i;  //cannot assign as 'i' does not exist in this context
 }

Więc dlaczego nie mogę używać wartości 'i' poza blokiem for, jeśli nie pozwala mi to zadeklarować zmiennej o tej nazwie?

Myślałem, że zmienna iteratora używana przez pętlę for jest ważna tylko w jej zakresie.

Author: Jeff B, 2011-11-03

9 answers

Powodem, dla którego nie możesz zdefiniować zmiennej o tej samej nazwie zarówno w pętli for, jak i poza pętlą for, Jest to, że zmienne w zewnętrznym zakresie są ważne w wewnętrznym zakresie. Oznacza to, że w pętli for byłyby dwie zmienne "i", gdyby było to dozwolone.

Zobacz: Lunety MSDN

Konkretnie:

Zakres zmiennej lokalnej zadeklarowanej w deklaracji zmiennej lokalnej (Pkt 8.5.1) jest blokiem, w którym deklaracja występuje.

I

Zakres zmiennej lokalnej zadeklarowanej w For-inicjalizatorze for Instrukcja (sekcja 8.8.3) jest For-initializer, for-condition, for-iterator oraz zawarte oświadczenie for.

A także: deklaracje zmiennych lokalnych (sekcja 8.5.1 specyfikacji C#)

Konkretnie:

Zakres zmiennej lokalnej zadeklarowanej w local-variable-declaration jest blokiem, w którym występuje deklaracja. błędem jest odwoływanie się do zmiennej lokalnej w pozycji tekstowej, która poprzedza local-variable-deklarator zmiennej lokalnej. w zakresie zmienna lokalna, jest to błąd w czasie kompilacji deklarowania innej lokalnej zmienna lub Stała o tej samej nazwie.

(podkreślenie moje.)

Co oznacza, że zakres i wewnątrz Twojej pętli for jest pętlą for. Podczas gdy zakres i poza Twoją pętlą for jest całą główną metodą plus pętla for. Oznacza to, że wewnątrz pętli występują dwa wystąpienia i, które są nieprawidłowe zgodnie z powyższym opisem.

Powodem, dla którego nie wolno Ci robić int A = i;, jest to, że int i jest przeznaczony tylko do użytku w pętli for. W ten sposób nie jest już dostępny poza pętlą for.

Jak widać oba te problemy są wynikiem scopingu; pierwszy numer (int i = 4;) wynikałoby z dwóch zmiennych i w zakresie pętli for. Natomiast int A = i; spowodowałoby dostęp do zmiennej, która jest poza zakresem.

Możesz zamiast tego zadeklarować i, że ma on zasięg do całej metody, a następnie użyć go zarówno w metodzie, jak i w zakresie pętli for. Pozwoli to uniknąć złamania obu zasad.

public static void Main()
{
    int i;

    for (i = 0; i < 5; i++)
    {

    }

    // 'i' is only declared in the method scope now, 
    // no longer in the child scope -> valid.
    i = 4;

    // 'i' is declared in the method's scope -> valid. 
    int A = i;
}

EDIT :

Kompilator C# można oczywiście zmienić, aby ten kod mógł się poprawnie skompilować. Po tym wszystkim jest to ważne:

for (int i = 0; i < 5; i++)
{
    Console.WriteLine(i);
}

for (int i = 5; i > 0; i--)
{
    Console.WriteLine(i);
}

Ale czy naprawdę byłoby korzystne dla czytelności i konserwacji kodu, aby móc pisać kod taki jak:

public static void Main()
{
    int i = 4;

    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine(i);
    }

    for (int i = 5; i > 0; i--)
    {
        Console.WriteLine(i);
    }

    Console.WriteLine(i);
}

Pomyśl o potencjale błędów tutaj, czy ostatni i wydrukować 0 lub 4? Teraz jest to bardzo mały przykład, który jest dość łatwy do naśladowania i śledzenia, ale jest zdecydowanie o wiele mniej czytelny i możliwy do utrzymania niż zadeklarowanie zewnętrznej i inną nazwą.

Uwaga:

Uwaga, C # ' s scoping rules różni się od C++'S scoping rules. W C++ zmienne są tylko w zakresie, od którego są deklarowane do końca bloku. Co uczyniłoby Twój kod poprawnym konstruktem w C++.

 120
Author: Johannes Kommer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-15 18:48:39

ODPOWIEDŹ J. Kommera jest poprawna: krótko mówiąc, jest nielegalne, aby zmienna lokalna była deklarowana w przestrzeni deklaracji zmiennych lokalnych, która pokrywa się zinną przestrzenią deklaracji zmiennych lokalnych o tej samej nazwie.

Istnieje dodatkowa reguła C#, która jest naruszana również tutaj. Dodatkową zasadą jest to, że jest nielegalne, aby prosta nazwa odnosiła się do dwóch różnych encji wewnątrz dwóch różnych nakładających się zmiennych lokalnych miejsca w klasyfikacji generalnej więc nie tylko twój przykład jest nielegalny, to też jest nielegalne:

class C
{
    int x;
    void M()
    {
        int y = x;
        if(whatever)
        {
            int x = 123;

Ponieważ teraz prosta nazwa " x "została użyta w przestrzeni deklaracji zmiennej lokalnej "y", aby oznaczać dwie różne rzeczy - " to.X "i lokalne "x".

Zobacz http://blogs.msdn.com/b/ericlippert/archive/tags/simple+nazwy / do dalszej analizy tych zagadnień.

 29
Author: Eric Lippert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-03 15:05:12

Istnieje sposób deklarowania i używania i wewnątrz metody po pętli:

static void Main()
{
    for (int i = 0; i < 5; i++)
    {

    }

    {
        int i = 4;
        int A = i;
    }
}

Można to zrobić w Javie(może pochodzić z C nie jestem pewien). Jest to oczywiście trochę bałagan ze względu na nazwę zmiennej.

 13
Author: Chris S,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-03 13:09:34

Jeśli zadeklarowałeś i przed Twoją for pętlą, czy uważasz, że nadal powinno być poprawne deklarowanie jej wewnątrz pętli?

Nie, ponieważ wtedy zakres tych dwóch nakładałby się na siebie.

Jeśli chodzi o to, że nie jest w stanie zrobić int A=i;, to po prostu dlatego, że i istnieje tylko w pętli for, tak jak powinno.

 7
Author: Widor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-03 09:12:29

Oprócz odpowiedzi J. Kommera (+1 btw). Jest to w standardzie dla zakresu sieciowego:

Block jeśli zadeklarujesz zmienną wewnątrz konstrukcji blokowej, takiej jak instrukcja If, zakres tej zmiennej jest tylko do końca bloku. Dożywocie trwa do zakończenia procedury.

Procedure Jeśli zadeklarujesz zmienną wewnątrz procedury, ale poza dowolną instrukcją If, zakres jest do końca Sub lub End Funkcja. Żywotność zmienna jest aż do zakończenia procedur.

Tak więc int i dekalarowany w nagłówku pętli for będzie w zasięgu tylko podczas bloku pętli for, ale jego żywotność trwa aż do zakończenia kodu Main().

 7
Author: ChrisBD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-03 09:47:02

Najprostszym sposobem myślenia o tym jest przesunięcie zewnętrznej deklaracji I powyżej pętli. To powinno stać się oczywiste.

Tak czy siak jest ten sam zakres, więc nie da się tego zrobić.

 5
Author: Andrew Barber,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-03 09:11:50

Również reguły C#są często niepotrzebne, jeśli chodzi o ścisłe programowanie, ale są po to, aby Twój kod był czysty i czytelny.

Na przykład, mogli zrobić to tak, że jeśli zdefiniujesz go po pętli, to jest ok, jednak ktoś, kto czyta Twój kod i pominął linię definicji, może pomyśleć, że ma to związek ze zmienną pętli.

 4
Author: Guy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-03 09:13:18

Odpowiedź Kommera jest technicznie poprawna. Parafrazuję to żywą metaforą ślepego ekranu.

Pomiędzy blokiem for i zamykającym się blokiem zewnętrznym istnieje jednokierunkowa ślepa osłona, tak że kod znajdujący się wewnątrz bloku for może zobaczyć kod zewnętrzny, ale kod w bloku zewnętrznym nie może zobaczyć kodu wewnątrz.

Ponieważ Zewnętrzny kod nie może widzieć wewnątrz, nie może używać niczego zadeklarowanego wewnątrz. Ale ponieważ kod w for-bloku może widzieć zarówno wewnątrz, jak i na zewnątrz, a zmienna zadeklarowana w obu miejscach nie może być jednoznacznie użyta przez nazwę.

Więc albo tego nie widzisz, albo c# !

 2
Author: explorer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-04 07:45:38

Spójrz na to w ten sam sposób, jakbyś mógł zadeklarować int w bloku using:

using (int i = 0) {
  // i is in scope here
}
// here, i is out of scope

Jednakże, ponieważ int nie implementuje IDisposable, nie można tego zrobić. Może to jednak pomóc komuś wyobrazić sobie, jak zmienna int jest umieszczona w prywatnym zakresie.

Innym sposobem byłoby powiedzieć,

if (true) {
  int i = 0;
  // i is in scope here
}
// here, i is out of scope

Mam nadzieję, że to pomoże wyobrazić sobie, co się dzieje.

Bardzo podoba mi się Ta funkcja, ponieważ deklarowanie {[2] } z wnętrza for pętli sprawia, że kod jest ładny i mocno.

 0
Author: jp2code,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-03 13:38:32