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

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

string var = "New VAR!";

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

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

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

Почему C# не могущий дифференцироваться между двумя объемами? Должен первое ЕСЛИ объем не быть абсолютно отдельным от остальной части метода?

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

66
задан Nicolas Raoul 17 February 2017 в 11:50
поделиться

3 ответа

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

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

Теоретически приемлемые (но все же недействительные в отношении C#):

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!";
}

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

45
ответ дан 24 November 2019 в 15:05
поделиться

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

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

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

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

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

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

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

Чтобы добавить оскорбление травме, я помню, что у GCC и VS6 были разные мнения о том, где находится переменная счетчика в циклах 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. В любом случае, C# кое-что подчистил, что хорошо.

11
ответ дан 24 November 2019 в 15:05
поделиться

Разве это не просто просто не так?

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

Штаты спецификации:


для каждого возникновения данного идентификатор как простое имя в выражение или декларатор, в пределах Локальное переменное пространство объявления из этого возникновения, каждый другое вхождение того же идентификатор как простое имя в выражение или декларатор должны относиться к тому же юридическое лицо. Это правило гарантирует, что Значение имени всегда одинаково В данном блоке переключатель блока, для-, foreach- или использование-оператор или Анонимная функция.


Почему C # невозможно дифференцировать между двумя спецификациями?

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

Если первое, если прицел не будет полноценным отделением от остальной части метода?

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

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

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

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

Например:

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

это совершенно законно; Область применения внешнего 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», только косвенно .

34
ответ дан 24 November 2019 в 15:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: