2012-04-12 3 views
4

Проект ASP.NET 4.0 Webforms. У меня есть код в моем коде.Локальная переменная с тем же именем, что и переменная экземпляра = неожиданные результаты

public partial class _Default : System.Web.UI.Page 
{ 
    private string testVar; 

    protected override void OnInit(EventArgs e) 
    { 
     string testVar = "test"; 
    } 

    protected void Page_Load(object sender, EventArgs e) 
    { 
     var whatsTheValue = testVar; 
    } 
} 

Я устанавливаю точку прерывания внутри каждого метода. Когда локальная переменная testVar установлена ​​в OnInit, если я быстро просмотрю переменную экземпляра, она также имеет значение «test». Когда я играю до Page_Load, значение переменной экземпляра составляет null.

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

Может кто-нибудь объяснить мне такое поведение?

+1

Как вы смотрите переменную экземпляра? Возможно, вы смотрите вместо локальной переменной? – Polyfun

+0

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

+0

ReSharper дает предупреждения об этом, как и мой здравый смысл.После изучения поведения, пожалуйста, измените имя одного из них :-) –

ответ

6

Если я быстро просмотрю переменную экземпляра, она также имеет значение «test». Когда я играю в Page_Load, значение переменной экземпляра равно null.

Вы не видеть переменную экземпляра, вы видите локальную переменную. Переменная экземпляра никогда не устанавливается, поскольку переменная с локальным охватом скрывает переменную экземпляра во время ее продолжительности.

Из спецификации:

3.7.1 Имя скрывается

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

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

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

+3

Правда. Если вы хотите увидеть переменную-член, введите 'this.testVar' в окно Watch. –

+0

Интересно. Спасибо за ответ, это хорошо знать. –

+0

Разве это не ошибка отладчика? Если я наведите указатель мыши на переменную, не должен ли она показывать значение переменной под мышью, а не показывать значение локальной переменной? Такое же поведение в VS 2015. –

0

'm установка точки останова внутри каждого метода. Когда локальная переменная, testVar, установлена ​​в OnInit, если я быстро просмотрю переменную экземпляра, она также имеет значение «test». Когда я играю в Page_Load, значение переменной экземпляра равно null.

Дело в том, что Watch of VisualStudio, основанный на присвоении имени переменной в момент подсказки точки останова. Поэтому, когда точка останова включена в метод OnInit, даже если отладчик показывает вам, что значение переменной имеет, это неправильная визуализация данных.

Так что все в порядке, только визуализации неверных данных.

11

Ответы BrokenGlass, конечно, правильные; вы не смотрите на поле в первую очередь. Местное скрывает поле.

Однако хочу отметить, что никто не решает эту часть вашего вопроса:

Я на самом деле удивлен, что он компилирует. Я бы ожидал увидеть какое-то предупреждение о наличии двух переменных с тем же именем.

Компилятор C# не выдает сообщение об ошибке или предупреждении в этом случае, потому что это создаст форму «хрупкого отказа базового класса». Предположим, у вас есть этот код:

class B { } 

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

, где B - одна команда в вашей организации, а D - совершенно другая команда. Тогда один прекрасный день Сопровождающие из B решает добавить функцию, которая нуждается в защищаемом поле:

class B { protected int x; } 

При перекомпилировать D, она должна дать ошибку? Если это так, то кто-то из совершенно другой команды просто сломал ваш код! C# был тщательно разработан, чтобы свести к минимуму (хотя и не устранить) этот вид поломки.

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

class B { protected int x; } 
class D : B 
{ 
    void M() 
    { 
     x = 123; // meaning this.x inherited from B 
     if (whatever) 
     { 
      int x = 10; 
      ... 

Это незаконно, потому что теперь в теле М, простое имя x используется для обозначения как this.xи локальных переменной x. Поскольку это потенциально запутывает и плохо программирует, мы делаем ошибку. Тем не менее, если вы действительно хотите, чтобы после этого вы можете устранить ошибку, гарантируя, что обычаи в * совершенно отдельных блоков:

class B { protected int x; } 
class D : B 
{ 
    void M() 
    { 
     { 
      x = 123; // meaning this.x inherited from B 
     } 
     if (whatever) 
     { 
      int x = 10; 

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

+0

Отличное объяснение. Спасибо! –

Смежные вопросы