C # переменная область видимости: «x» не может быть объявлен в этой области, потому что это даст другое значение для «x» - PullRequest
65 голосов
/ 12 января 2010
if(true)
{
    string var = "VAR";
}

string var = "New VAR!";

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

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

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

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

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

Ответы [ 3 ]

45 голосов
/ 12 января 2010

Проблема здесь во многом связана с хорошей практикой и предотвращением непреднамеренных ошибок. По общему признанию, компилятор 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!";
}

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

34 голосов
/ 12 января 2010

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

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

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


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


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

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

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

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

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

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

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

Например:

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", только косвенно .

11 голосов
/ 12 января 2010

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

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

РЕДАКТИРОВАТЬ (некоторые примеры) -----

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

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

Теперь представьте, что функция на несколько строк длиннее, и может быть легко не заметить ошибку. Компилятор никогда не жалуется (не в старые времена, не уверен в новых версиях C ++), и функция всегда возвращает 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 # убрал несколько вещей, и это хорошо.

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