Смешивание области видимости в С#

У меня есть два примера кода. Первый не компилируется, но второй делает.

Пример кода 1 (не компилируется)

public void MyMethod(){
    int i=10;

    for(int x=10; x<10; x++) {
        int i=10; // Point1: compiler reports error
        var objX = new MyOtherClass();
    }

    var objX = new OtherClassOfMine(); // Point2: compiler reports error
}

Я понимаю, почему компилятор сообщает об ошибке в Point1. Но я не понимаю, почему он сообщает об ошибке в Point2. И если вы говорите, что это из-за организации внутри MSIL, то почему второй пример кода компилируется?

Пример кода 2 (компиляции)

public void MyMethod(){

    for(int x=10; x<10; x++) {
        int i=10; 
        var objX = new MyOtherClass();
    }

    for(int x=10; x<10; x++) {
        int i=10; 
        var objX = new MyOtherClass();
    }
}

Если простые правила области видимости переменной применяются в примере кода 2, то почему эти правила не применяются к Code Sample 1?

Ответы

Ответ 1

Здесь есть два соответствующих правила.

Первое соответствующее правило:

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

(И еще один ответ на этой странице вызывает другое место в спецификации, где мы вызываем это снова.)

Этого достаточно, чтобы сделать это незаконным, но на самом деле второе правило делает это незаконным.

Второе релевантное правило в С#:

Для каждого случая данного идентификатор как простое имя в выражения или декларатора в пределах локальное пространство декларации переменных, немедленно закрывающий блок, или switch-block этого события, каждое другое возникновение того же идентификатор как простое имя в выражения или декларатора в пределах немедленно вставляя блок или блок switch должен ссылаться на тот же организация. Это правило гарантирует, что значение имени всегда одно и то же в пределах данного блока, блок переключателя, for-, foreach- или use-statement или анонимная функция.

Вам также нужно знать, что for-loop рассматривается так, как будто есть все невидимые фигурные скобки.

Теперь, когда мы это знаем, давайте аннотируем ваш код:

public void MyMethod()
{ // 1
    int i=10; // i1
    { // 2 -- invisible brace
      for(int x=10; x<10; x++) // x2
      { // 3
        int i=10;  // i3
        var objX = new MyOtherClass(); // objX3
      } // 3
    } // 2
    var objX = new OtherClasOfMine(); // objX1
} // 1

У вас есть три "простых имени", i, x и objX. У вас есть пять переменных, которые я обозначил i1, x2, i3, objX3 и objX1.

Самый внешний блок, который содержит использование я и objX, является блоком 1. Поэтому в блоке 1 я и objX должны всегда ссылаться на одно и то же. Но они этого не делают. Иногда я ссылаюсь на i1, а иногда на i3. То же самое с objX.

x, однако, только когда-либо означает x2 в каждом блоке.

Кроме того, обе переменные "i" находятся в одном и том же локальном пространстве декларации переменных переменных, как и "objX".

Следовательно, эта программа является ошибкой несколькими способами.

В вашей второй программе:

public void MyMethod()
{ // 1
    { // 2 -- invisible 
      for(int x=10; x<10; x++)   // x2
      { // 3
        int i=10;  // i3
        var objX = new MyOtherClass(); // objX3
      } //3 
    } // 2
    { // 4 -- invisible
      for(int x=10; x<10; x++)  // x4
      { // 5
        int i=10;  // i5
        var objX = new MyOtherClass();  // objX5
      } //5
   } // 4
} // 1

Теперь у вас снова три простых имени и шесть переменных.

Внешние блоки, которые сначала содержат использование простого имени x, являются блоками 2 и 4. На протяжении второго блока x относится к x2. На протяжении блока 4 x относится к x4. Поэтому это законно. То же самое с я и objX - они используются в блоках 3 и 5 и означают разные вещи в каждом. Но нигде одно и то же простое имя не означает, что в одном блоке означают две разные вещи.

Теперь вы можете заметить, что с учетом всего блока 1, x используется для обозначения как x2, так и x4. Но не упоминается x, которое находится внутри блока 1, но НЕ также внутри другого блока. Поэтому мы не считаем несогласованное использование в блоке 1 релевантным.

Кроме того, ни одно из пространств декларации не перекрывается незаконными способами.

Поэтому это законно.

Ответ 2

Из спецификации языка С#...

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

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

Ответ 3

Вам разрешено использовать одно и то же имя переменной в неперекрывающихся областях. Если одна область перекрывает другую, вы не можете иметь одну и ту же переменную, объявленную в обоих. Причина этого заключается в том, чтобы предотвратить случайное использование уже используемого имени переменной во внутренней области, например, с помощью i в первом примере. Это не значит, чтобы предотвратить ошибку objX, поскольку это, по общему признанию, не должно быть очень запутанным, но ошибка является следствием того, как применяется правило. Компилятор рассматривает objX как имеющий происхождение во всем объеме, в котором он объявлен как до, так и после его объявления, а не только после.

Во втором примере две петли for имеют независимые, не перекрывающиеся области, поэтому вы можете повторно использовать i и objX во втором цикле. Это также причина, по которой вы можете повторно использовать x в качестве счетчика циклов. Очевидно, что это было бы немым ограничением, если бы вам приходилось составлять разные имена для каждого цикла стиля for(i=1;i<10;++i) в функции.

В личной заметке я нахожу эту ошибку раздражающей и предпочитаю, чтобы C/С++ позволял вам делать то, что вам нужно, путаница будет проклята.

Ответ 4

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