Область видимости переменной C # не соответствует? - PullRequest
6 голосов
/ 17 февраля 2012

C # весьма придирчив, когда дело доходит до переменной области видимости.Как это возможно, что он принимает этот код:

class Program
{
    int x = 0;

    void foo()
    {
        int x = 0;
        x = 1;

        Console.WriteLine(x);
    }
}

Если вы спросите меня, это очевидный конфликт имен.Тем не менее компилятор (VS 2010) принимает это.Почему?

Ответы [ 7 ]

18 голосов
/ 17 февраля 2012

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

http://ericlippert.com/2009/11/02/simple-names-are-not-so-simple/

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

Чтобы ответить на ваш конкретный вопрос: компилятор определенно может обнаружить этот конфликт.Фактически, обнаруживает этот конфликт:

class P
{
    int x;
    void M()
    {
        x = 123; // The author intends "this.x = 123;"
        int x = 0;
    }
}

Эквивалентная программа на C ++ была бы допустимой C ++, потому что в C ++ локальная переменная входит в область действия в точке ее объявления.В C # локальная переменная находится в области действия всего своего блока, и ее использование до объявления недопустимо.Если вы попытаетесь скомпилировать эту программу, вы получите:

error CS0844: Cannot use local variable 'x' before it is declared.
The declaration of the local variable hides the field 'P.x'.

См .: локальная декларация скрывает поле .Компилятор это знает.Так почему же в вашем случае не ошибка скрыть поле?

Давайте предположим, что это должно быть ошибкой.Должна ли это быть также ошибкой?

class B
{
    protected int x;
}
class D : B
{
    void M()
    {
        int x;
    }
}

Поле x является членом D через наследование от B. Так что это также должно быть ошибкой, верно?

Теперь предположим, что у вас есть эта программа, созданная Foo Corporation:

class B
{
}

, и эта программа, созданная Bar Corporation:

class D : B
{
    void M()
    {
        int x;
    }
}

, которая компилируется.Теперь предположим, что Foo Corp обновляет свой базовый класс и отправляет вам новую версию:

class B
{
    protected int x;
}

Вы говорите мне, что каждый производный класс, содержащий локальную переменную с именем x, теперь не сможет скомпилироваться?

Это было бы ужасно.Мы должны разрешить локальным переменным затенять членов.

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

12 голосов
/ 17 февраля 2012

Это не конфликт имен: в C # локальные переменные имеют приоритет над переменными экземпляра с тем же именем, потому что их scope уже.

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

См. Документацию по Ссылочное соответствие для получения подробной информации по этому вопросу.

3 голосов
/ 17 февраля 2012

Это нормально.

В конструкторах я часто использую одно и то же.

public Person(string name) {
  this.name = name;
}

В противном случае было бы невозможно объявить параметры метода, которые названы как переменные-члены.

1 голос
/ 17 февраля 2012

Спецификация C # 4.0 говорит об этом как скрытие области через вложение:

3.7.1.1 Скрытие через вложение

Скрытие имени через вложение может происходить какрезультат вложения пространств имен или типов в пространствах имен, в результате вложения типов в классах или структурах, а также в результате объявления параметров и локальных переменных.В примере

class A {
    int i = 0;
    void F() {
        int i = 1;
    }
    void G() {
        i = 1;
    }
}

в методе F переменная экземпляра i скрыта локальной переменной i, но в методе G я все еще ссылаюсь на переменную экземпляра.

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

В примере

class Outer {
    static void F(int i) {}
    static void F(string s) {}
    class Inner
    {
        void G() {
            F(1);           // Invokes Outer.Inner.F
            F("Hello");     // Error
        }
        static void F(long l) {}
    }
}

вызов F (1) вызывает F, объявленный во Inner, потому что все внешние вхождения F скрыты внутренним объявлением.По той же причине вызов F («Hello») приводит к ошибке во время компиляции.

0 голосов
/ 17 февраля 2012

Это не является двусмысленным, локальная переменная будет использоваться в вашей функции. Если вам нужно получить переменную класса, this.x разрешает разрешение имен.

0 голосов
/ 17 февраля 2012

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

0 голосов
/ 17 февраля 2012

Нет конфликта имен.Компилятор всегда принимает nearest / наименьшую переменную области видимости.

В этом случае это переменная x, которую вы объявляете в foo.Каждая переменная может быть доступна определенным образом, поэтому нет конфликта имен.

Если вы хотите получить доступ к внешнему x, вы можете использовать this.x.

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