Почему С# не позволяет мне использовать одно и то же имя переменной в разных областях?

Как например:

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

Matrix matrix = new Matrix();

Компилятор предупреждает меня, говоря:

"Локальная переменная с именем" matrix "не может быть объявлена ​​в этой области, потому что она будет иметь другое значение" matrix ", которое уже используется в области" child ", чтобы обозначить что-то еще.

Не эти переменные в разных областях, поэтому я не смог бы получить доступ к первой matrix извне оператора if?

Ответ 1

Ответы, данные до сих пор, очень сбивают с толку. Правильный анализ проблемы начинается с чтения сообщения об ошибке. Сообщение об ошибке сообщает вам, что на самом деле неправильно:

"Локальная переменная с именем" matrix "не может быть объявлена ​​в этой области, потому что она придаст другой смысл" матрице ", которая уже используется в области" child ", чтобы обозначить что-то еще.

Прочитайте внимательно . Он сообщает вам, какое именно правило С# нарушается, а именно, что вам не разрешено использовать одно и то же имя для обозначения двух разных вещей в одной и той же области. (На самом деле, сообщение об ошибке несколько неверно, оно должно сказать "локальное пространство декларации переменных", где указано "scope", но это довольно многословно.)

Это правило задокументировано в спецификации С# 4.0, раздел 7.6.2.1: Простые имена, Инвариантное значение в блоках.

(Также недопустимо иметь две локальные переменные с одним и тем же именем в перекрывающихся пространствах объявлений. Компилятор также может сообщать об этой ошибке, но в этом случае он сообщает об общей ошибке.)

Не эти переменные в разных областях, поэтому я не смог бы получить доступ к первой матрице извне оператора if?

Да. Это утверждение верно, но не имеет значения. Ошибка здесь заключается в том, что одно и то же простое имя использовалось для обозначения двух разных вещей в одном и том же пространстве объявления переменных переменных.

Рассмотрим этот сценарий:

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

Такая же сделка. Ошибка здесь заключается в том, что простое имя "x" использовалось во внешнем пространстве декларации для ссылки на this.x и использовалось во внутреннем пространстве декларации для обозначения "локальной переменной". Используя одно и то же простое имя, чтобы ссылаться на две разные вещи в одном и том же пространстве декларации - помните, внутреннее пространство декларации является частью внешнего, является как запутанным, так и опасным, и поэтому является незаконным.

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

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

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

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

Для получения дополнительной информации и интересной истории о жареной пище см.

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

Ответ 2

Я считаю, что это делается для того, чтобы избежать неясных ошибок или кода, который трудно читать.

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

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

Фактически вы можете найти это в MSDN:

Объем имени - это область текст программы, в пределах которого он можно сослаться на объект объявленный именем без квалификация имени. Области могут быть вложенным, а внутренний объем может переопределить значение имени из внешний охват. (Это не так, однако, удалите ограничение введенный Раздел 3.3, что в пределах вложенный блок невозможно объявить локальную переменную с тем же имя как локальная переменная в закрывающий блок.). Имя из внешний объем затем считается скрытым в области охвата текста программы по внутреннему объему и доступ к внешнее имя возможно только присвоить имя.

Добавлен акцент.

И, из раздела 3.3:

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

Добавлен акцент.

Итак, дело в том, что в то время как области различаются, пространство переменных одинаково.

Ответ 3

Вы всегда можете это сделать...

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

    {
        Matrix matrix = new Matrix(); 
    }
}

... Каждый набор фигурных скобок {} позволяет вам вложить еще один уровень охвата. Проблема, с которой вы сталкиваетесь, заключается в том, что вложенные области включают сферу их родителей. Если вы объявите сферу siblng, она сможет повторно использовать переменные внутри одного и того же родителя. Но, как указывали другие, это может запутаться позже.

Ответ 4

Представьте, что человек пытается прочитать этот код.

С точки зрения другого разработчика, пытающегося прочитать ваш код, вы можете понять, как смущать его было бы иметь две разные переменные с тем же именем? Даже если они представляют одно и то же, слишком сложно справиться с двумя вещами с тем же именем.

Ответ 5

Matrix matrix = new Matrix();

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

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

Из MSDN "A: Это правильное поведение и рассматривается в разделе 3.7 спецификации языка. В нем говорится:" Область локальной переменной, объявленной в объявлении локальной переменной (8.5.1), является блоком в котором происходит объявление "...." Такое поведение позволяет сделать неправильное повторное использование имен переменных (например, в разрезе и вставке) менее вероятным". (Http://blogs.msdn.com/b/csharpfaq/archive/2004/05/18/why-can-ti-use-the-same-variable-as-an-inner-loop-does.aspx)