Переменная, объявленная в цикле for, является локальной переменной? - PullRequest
134 голосов
/ 03 ноября 2011

Я использую C # довольно долгое время, но никогда не осознавал следующее:

 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
 }

Так почему я не могу использовать значение 'i' вне блока for, если оно не позволяет мне объявить переменную с этим именем?

Я думал, что переменная итератора, используемая циклом for, действительна только в своей области действия.

Ответы [ 9 ]

119 голосов
/ 03 ноября 2011

Причина, по которой вам не разрешено определять переменную с одинаковым именем как в цикле for, так и вне цикла for, заключается в том, что переменные во внешней области действия допустимы во внутренней области. Это означает, что в цикле for будут две переменные 'i', если это будет разрешено.

См .: Области MSDN

В частности:

Область локальной переменной, объявленной в объявлении локальной переменной (Раздел 8.5.1) - это блок, в котором происходит объявление.

и

Область действия локальной переменной, объявленной в for-инициализаторе for оператор (раздел 8.8.3) является инициализатором for, условием for, for-iterator и содержащийся оператор for.

А также: Объявления локальных переменных (Раздел 8.5.1 спецификации C #)

В частности:

Область локальной переменной, объявленной в объявлении локальной переменной блок, в котором происходит объявление. Ссылка на ошибку к локальной переменной в текстовой позиции, которая предшествует локальная переменная-декларатор локальной переменной. В рамках локальная переменная, это ошибка времени компиляции, чтобы объявить другую локальную переменная или константа с тем же именем.

(Акцент мой.)

Это означает, что область действия i внутри вашего цикла for является циклом for. Принимая во внимание, что область действия i вне вашего цикла for - это весь основной метод plus for-loop. Это означает, что у вас будет два вхождения i внутри цикла, что недопустимо в соответствии с приведенным выше.

Причина, по которой вам не разрешено делать int A = i;, заключается в том, что int i ограничивается только для использования в цикле for. Таким образом, он больше не доступен вне цикла for.

Как вы можете видеть, обе эти проблемы являются результатом определения области; первая проблема (int i = 4;) привела бы к двум i переменным в пределах цикла for. Принимая во внимание, что int A = i; приведет к доступу к переменной, которая находится вне области видимости.

Вместо этого вы можете объявить i как область действия всего метода, а затем использовать его как в методе, так и в области действия цикла for. Это позволит избежать нарушения любого из правил.

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

Компилятор C #, конечно, можно изменить, чтобы этот код компилировался вполне корректно. После всего этого действует:

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

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

Но было бы действительно полезно для вашего кода удобочитаемость и удобство сопровождения, чтобы иметь возможность писать такой код, как:

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

Подумайте о возможной ошибке здесь, последние i выводят 0 или 4? Теперь это очень маленький пример, за которым довольно легко следить и отслеживать, но он определенно гораздо менее удобен в обслуживании и читаемости, чем объявление внешнего i под другим именем.

N.B:

Обратите внимание, что правила области видимости в C # отличаются от Правила области видимости в C ++ . В C ++ переменные находятся только в области видимости от того, где они объявлены, до конца блока. Что сделает ваш код допустимой конструкцией в C ++.

29 голосов
/ 03 ноября 2011

J.Kommer отвечает правильно: вкратце, недопустимо, чтобы локальная переменная объявлялась в пространстве объявления локальной переменной , что перекрывает другое пространство объявления локальной переменной с локальным именем с таким же именем.

Здесь также нарушается дополнительное правило C #.Дополнительное правило заключается в том, что недопустимо, чтобы простое имя использовалось для ссылки на две разные сущности в двух разных перекрывающихся пространствах объявления локальных переменных. Так что не только ваш пример недопустим, но и недопустим:

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

Потому что теперь простое имя «x» использовалось внутри пространства объявления локальной переменной «y» для обозначения двух разных вещей - «this.x» и локального «x».

См. http://blogs.msdn.com/b/ericlippert/archive/tags/simple+names/ для более подробного анализа этих проблем.

13 голосов
/ 03 ноября 2011

Существует способ объявления и использования i внутри метода после цикла:

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

    }

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

Вы можете сделать это в Java (это может происходить из C, я не уверен).Это, конечно, немного грязно ради имени переменной.

7 голосов
/ 03 ноября 2011

В дополнение к ответу Дж. Коммера (+1 к слову). В стандарте для NET есть это:

block Если вы объявляете переменную в конструкции блока, например, в операторе If, область действия этой переменной остается только до конца блока. Срок действия до окончания процедуры.

Процедура Если вы объявляете переменную в процедуре, но вне какого-либо оператора If, область действия остается до End Sub или End Функция. Время жизни переменной до окончания процедур.

Таким образом, декалибровка int i в заголовке цикла for будет находиться в области видимости только во время блока цикла for, НО Срок его службы длится до завершения кода Main().

7 голосов
/ 03 ноября 2011

Если вы объявили i до вашего for цикла, думаете ли вы, что все еще допустимо объявить его внутри цикла?

Нет, потому что тогдаобласть видимости двух будет перекрываться.

Что касается невозможности сделать int A=i;, это просто потому, что i существует только в цикле for, как и должно быть.

5 голосов
/ 03 ноября 2011

Самый простой способ подумать об этом - переместить внешнюю декларацию I над циклом. Тогда это должно стать очевидным.

В любом случае это одна и та же область видимости, поэтому ее нельзя сделать.

4 голосов
/ 03 ноября 2011

Кроме того, правила C # во многих случаях не являются необходимыми для строгого программирования, но для того, чтобы ваш код был чистым и читабельным.

например, они могли бы сделать это так, что если вы определите его после цикла, тогда все в порядке, однако тот, кто читает ваш код и пропустил строку определения, может подумать, что это связано с переменной цикла.

2 голосов
/ 04 ноября 2011

Ответ Коммера технически верен.Позвольте мне перефразировать это с яркой метафорой слепого экрана.

Существует односторонний слепой экран между блоком for и окружающим внешним блоком, так что код внутри блока for может видеть внешний код, но код во внешнем блоке не может видеть код внутри.

Поскольку внешний код не может видеть внутри, он не может использовать ничего, объявленного внутри.Но поскольку код в блоке for может видеть как внутри, так и снаружи, переменная, объявленная в обоих местах, не может быть однозначно использована по имени.

Так что либо вы этого не видите, либо вы C #!

0 голосов
/ 03 ноября 2011

Смотрите на это так же, как если бы вы могли объявить int в блоке using:

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

Однако, поскольку int не реализуетIDisposable, это невозможно сделать.Это может помочь кому-то визуализировать, как переменная int размещается в закрытой области видимости.

Другой способ сказать:

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

Надеюсь, это поможет визуализировать происходящееon.

Мне очень нравится эта функция, так как объявление int внутри цикла for делает код красивым и сжатым.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...