Почему C # не позволяет мне использовать одно и то же имя переменной в разных областях? - PullRequest
15 голосов
/ 10 января 2011

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

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

Matrix matrix = new Matrix();

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

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

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

Ответы [ 5 ]

16 голосов
/ 10 января 2011

ОБНОВЛЕНИЕ: ответ ниже 2011 года является правильным для более ранних версий C #; в более поздних версиях описанное правило ответ был удален из C #. Команда разработчиков определила, что это правило вызывало у разработчиков больше путаницы, что приводило к таким вопросам, чем те, которые мешали ошибочные программы, даже если я значительно улучшил сообщения об ошибках, чтобы более четко диагностировать проблему.


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

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

Внимательно прочитайте . Это точно говорит вам, какое правило C # нарушается, а именно , что вы не можете использовать одно и то же имя для ссылки на две разные вещи в одной и той же области действия . (На самом деле, сообщение об ошибке немного неверно; оно должно содержать «пространство объявления локальной переменной», где оно говорит «область действия», но это довольно многословно.)

Это правило задокументировано в спецификации C # 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/

7 голосов
/ 10 января 2011

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

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

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

На самом деле, вы можете найти это в MSDN:

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

Добавлено выделение.

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

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

Выделение добавлено.

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

5 голосов
/ 10 января 2011

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

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

    {
        Matrix matrix = new Matrix(); 
    }
}

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

2 голосов
/ 10 января 2011

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

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

1 голос
/ 10 января 2011
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-t-i-use-the-same-variable-as-an-inner-loop-does.aspx)

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