Переменная, объявленная в for-loop, является локальной переменной?

Я использую С# довольно долго, но не понял следующее:

 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-loop, действительна только в своей области.

Ответ 1

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

Смотрите: Области MSDN

В частности:

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

и

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

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

В частности:

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

(Подчеркните мой.)

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

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

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

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

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 scope -> valid. 
    int A = i;
}

ИЗМЕНИТЬ

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

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:

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

Ответ 2

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

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

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/.

Ответ 3

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

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

    }

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

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

Ответ 4

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

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

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

Ответ 5

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

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

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

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

Ответ 6

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

То же самое в обоих направлениях, поэтому не может быть сделано.

Ответ 7

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

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

Ответ 8

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

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

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

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

Ответ 9

Посмотрите на это так же, как если бы вы могли объявить 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

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

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