与实例变量同名的局部变量=意外结果

时间:2012-04-12 15:45:50

标签: c# instance-variables local-variables

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

我在每个方法中设置一个断点。当局部变量testVarOnInit中设置时,如果我快速观察实例变量,它也具有值“test”。当我播放到Page_Load时,实例变量的值为null

我偶然碰到了这个,但这种行为让我感到困惑。我真的很惊讶它编译。我本来希望看到有关两个变量具有相同名称的警告。话虽如此,我更加困惑的是,实例变量在OnInit中获取赋值,但在退出该方法时立即丢失它。

有人可以向我解释这种行为吗?

3 个答案:

答案 0 :(得分:12)

BrokenGlass的答案当然是正确的;你不是首先看场。本地隐藏字段。

但是,我注意到没有人解决你问题的这一部分:

  

我真的很惊讶它编译。我本来希望看到有关两个变量具有相同名称的警告。

在这种情况下,C#编译器不会发出错误或警告,因为这样做会导致“脆弱的基类失败”。假设你有这段代码:

class B { }

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

其中B由您组织中的一个团队制作,D由完全不同的团队制作。然后有一天,B的维护者决定添加一个需要受保护字段的功能:

class B { protected int x; }

当您重新编译D时,它是否会出错?如果确实如此,那么完全不同的团队中的某个人就打破了您的代码! C#经过精心设计,可以最大限度地减少(但不能消除)这种破损。

请注意,如果您在同一代码块中重复使用x表示两个不同的内容,则C# 会出错。例如:

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

这是非法的,因为现在在M的正文中,简单名称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;

现在块不重叠,因此编译器假定您知道自己在做什么并允许代码编译。

答案 1 :(得分:7)

  

如果我快速观察实例变量,它也有值“test”。   当我播放到Page_Load时,实例变量的值是   空。

看到实例变量,您会看到本地变量。永远不会设置实例变量,因为本地范围的变量在其范围生存期内隐藏了实例变量。

来自规范:

  

3.7.1名称隐藏

     

实体的范围通常包含比实体的声明空间更多的程序文本。特别是,   实体的范围可能包括引入新的声明   声明空间包含同名实体。的这种   声明导致原始实体隐藏。反过来,   当一个实体没有被隐藏时,它被认为是可见的。

     

名称隐藏   当范围通过嵌套重叠并且范围重叠时发生   通过继承。这两种隐藏的特点   将在以下各节中介绍。

     

通过嵌套隐藏名称可能是嵌套的结果   命名空间中的命名空间或类型,作为嵌套类型的结果   在类或结构中,以及参数和本地的结果   变量声明

答案 2 :(得分:0)

  

在每个方法中设置一个断点。当局部变量时,   testVar,在OnInit中设置,如果我快速观察实例变量,它   也有“测试”的价值。当我播放Page_Load时,   实例变量的值为null。

事情是VisualStudio的Watch基于在断点提示的瞬间命名变量。因此,当在OnInit方法中使用断点时,即使调试器显示 instance 变量具有值,这也是数据的错误可视化

所以一切都很好,只有数据的可视化不正确。