Область видимости переменной С#: "x" не может быть объявлена ​​в этой области, потому что она придавала бы значение "x",

if(true)
{
    string var = "VAR";
}

string var = "New VAR!";

Это приведет к:

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

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

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

Я не могу вызывать var извне if, поэтому сообщение об ошибке неверно, потому что первый var не имеет значения во второй области.

Ответ 1

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

Учтите, что если объявление var в родительской области было перед оператором if, возник бы неразрешимый конфликт имен. Компилятор просто не проводит различия между следующими двумя случаями. Анализ выполняется исключительно на основе объема, а не порядка декларации/использования, как вы, кажется, ожидаете.

Теоретически приемлемый (но все же недопустимый для С#):

if(true)
{
    string var = "VAR";
}

string var = "New VAR!";

и неприемлемым (поскольку он будет скрывать родительскую переменную):

string var = "New VAR!";

if(true)
{
    string var = "VAR";
}

оба обрабатываются точно так же в терминах переменных и областей.

Теперь, есть ли какая-то реальная причина в этом секвенарио, почему вы не можете просто присвоить одной из переменных другое имя? Я предполагаю (надеюсь), что ваши фактические переменные не называются var, поэтому я действительно не вижу в этом проблемы. Если вы по-прежнему намерены повторно использовать одно и то же имя переменной, просто поместите их в области родства:

if(true)
{
    string var = "VAR";
}

{
    string var = "New VAR!";
}

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

Ответ 2

разве это просто неправильно?

Нет, это совсем не так. Это правильная реализация раздела 7.5.2.1 спецификации С#, "Простые имена, значения инвариантов в блоках".

В спецификации указано:


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


Почему С# не может отличить две области?

Вопрос бессмыслен; очевидно, что компилятор способен различать две области. Если компилятор не смог провести различие между двумя областями, то как может возникнуть ошибка? В сообщении об ошибке указано, что существуют две разные области видимости, поэтому диапазоны были дифференцированы!

Если первая область IF не может быть полностью отделена от остальной части метода?

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

Я не могу вызывать var извне if, поэтому сообщение об ошибке неверно, потому что первый var не имеет отношения к вторая область.

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

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

Например:

class C 
{
    int x;
    void M()
    { 
        int x = 123;
    }
}

Это совершенно законно; область внешнего x перекрывает область внутреннего x, но это не ошибка. Ошибка:

class C 
{
    int x;
    void M()
    { 
        Console.WriteLine(x);
        if (whatever)
        {
            int x = 123;
        }
    }
}

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

Мы разрешаем параллельным блокам содержать одно и то же простое имя, используемое двумя разными способами; это законно:

class C 
{
    int x;
    void M()
    { 
        if (whatever)
        {
            Console.WriteLine(x);
        }
        if (somethingelse)
        {
            int x = 123;
        }
    }
}

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

Ответ 3

Это действительно в С++, но источник для многих ошибок и бессонных ночей. Я думаю, что ребята из С# решили, что лучше бросить предупреждение/ошибку, поскольку в подавляющем большинстве случаев это ошибка, а не что-то, чего действительно хочет кодер.

Здесь интересная дискуссия о том, в каких частях спецификации возникает эта ошибка.

EDIT (некоторые примеры) -----

В С++ допустимо следующее (и не имеет значения, является ли внешнее объявление до или после внутренней области, оно будет более интересным и подверженным ошибкам, если оно раньше).

void foo(int a)
{
    int count = 0;
    for(int i = 0; i < a; ++i)
    {
        int count *= i;
    }
    return count;
}

Теперь представьте, что функция имеет несколько строк дольше, и было бы легко не заметить ошибку. Компилятор никогда не жалуется (а не на старые времена, не уверен в более новых версиях С++), и функция всегда возвращает 0.

Поведение, очевидно, является ошибкой, поэтому было бы хорошо, если бы программа С++-lint или компилятор указывали на это. Если это не ошибка, ее легко обойти, просто переименовав внутреннюю переменную.

Чтобы добавить оскорбление к травме, я помню, что GCC и VS6 имели разные мнения о том, где принадлежала переменная счетчика in for. Один сказал, что он принадлежит внешнему виду, а другой сказал, что нет. Немного раздражает работа над кросс-платформенным кодом. Позвольте мне привести еще один пример, чтобы поддерживать подсчет строки.

for(int i = 0; i < 1000; ++i)
{
    if(array[i] > 100)
        break;
}

printf("The first very large value in the array exists at %d\n", i);

Этот код работал в VS6 IIRC, а не в GCC. Во всяком случае, С# очистил несколько вещей, что хорошо.