7

ASP.NET 4.0 Webforms project. I have the following in my code-behind.

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

I'm setting a break point inside each method. When the local variable, testVar, is set in OnInit, if I quick watch the instance variable, it also has the value "test". When I play through to Page_Load, the instance variable's value is null.

I ran across this by accident but the behavior is confusing to me. I'm actually surprised that it compiles. I would have expected to see some sort of warning about having two variables with the same name. That being said, it's even more confusing to me that the instance variable picks up the assignment in OnInit, but then immediately loses it when that method is exited.

Can someone explain this behavior to me?

Jeremy Wiggins
  • 7,239
  • 6
  • 41
  • 56
  • 1
    How are you watching the instance variable? Perhaps you are watching the local variable instead? – Polyfun Apr 12 '12 at 15:48
  • I think it's more likely that you are either looking at the local variable in the debugger and not the instance variable, or else the debugger is bugged and is displaying the value from the local variable as the value of the instance variable. – Matt Burland Apr 12 '12 at 15:48
  • ReSharper gives warnings about this sort of thing, as does my common sense. After learning the behaviour, please change the name of one of those :-) – Adam Houldsworth Apr 12 '12 at 15:49
  • This is purely a guess, but I guess that what you are seeing is the debugger being confused. Have you tried to insert f.e.: String testVar2 = this.testVar; in OnInit()? My guess is you'd still see testVar2 being null. As to why it compiles, it is not illegal to have a local variable with the same name as an instance variable. – Reonekot Apr 12 '12 at 15:51

3 Answers3

16

BrokenGlass's answer is of course correct; you are not looking at the field in the first place. The local hides the field.

However, I note that no one has addressed this portion of your question:

I'm actually surprised that it compiles. I would have expected to see some sort of warning about having two variables with the same name.

The C# compiler does not issue an error or warning in that case because doing so would create a form of "brittle base class failure". Suppose you have this code:

class B { }

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

where B is made by one team in your organization, and D is made by an entirely different team. Then one day the maintainers of B decides to add a feature that needs a protected field:

class B { protected int x; }

When you recompile D, should it give an error? If it does, then someone on a completely different team just broke your code! C# has been carefully designed to minimize (though not eliminate) this kind of breakage.

Note that C# does give an error if you re-use x to mean two different things in the same code block. For example:

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

That's illegal because now in the body of M, the simple name x is used to mean both this.x and local variable x. Because this is potentially confusing and a bad programming practice, we make it an error. However, if you really want that then you can eliminate the error by ensuring that the usages are in *entirely separate blocks:

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

Now the blocks do not overlap and therefore the compiler assumes that you know what you are doing and allows the code to compile.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
8

if I quick watch the instance variable, it also has the value "test". When I play through to Page_Load, the instance variable's value is null.

You are not seeing the instance variable, you see the local variable. The instance variable is never set, because the locally scoped variable is hiding the instance variable during it's scope lifetime.

From the spec:

3.7.1 Name hiding

The scope of an entity typically encompasses more program text than the declaration space of the entity. In particular, the scope of an entity may include declarations that introduce new declaration spaces containing entities of the same name. Such declarations cause the original entity to become hidden. Conversely, an entity is said to be visible when it is not hidden.

Name hiding occurs when scopes overlap through nesting and when scopes overlap through inheritance. The characteristics of the two types of hiding are described in the following sections.

Name hiding through nesting can occur as a result of nesting namespaces or types within namespaces, as a result of nesting types within classes or structs, and as a result of parameter and local variable declarations

Community
  • 1
  • 1
BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
  • 3
    True. If you want to see the member variable, type `this.testVar` into your Watch window. – Mr Lister Apr 12 '12 at 15:49
  • Isn't this a debugger bug? If i hover over a variable, shouldn't it show the value of the variable under the mouse instead of showing the value of the local variable? Same behaviour in VS 2015. – Tim Schmelter Jun 29 '16 at 10:14
0

'm setting a break point inside each method. When the local variable, testVar, is set in OnInit, if I quick watch the instance variable, it also has the value "test". When I play through to Page_Load, the instance variable's value is null.

The thing is that the Watch of VisualStudio based on naming of the variable in the moment of breakpoint hint. So when breakpoint is hitted inside OnInit method, even if debugger shows you that instance variable has value, this is a wrong visualization of the data.

So everything is fine, only visualization of the data is not correct.

Tigran
  • 61,654
  • 8
  • 86
  • 123