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

本文关键字:局部变量 意外 结果 实例 变量 | 更新日期: 2023-09-27 18:30:58

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 中获取赋值,但在退出该方法时立即丢失它。

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

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

Broken Glass的答案当然是正确的;你一开始就没有看这个领域。本地隐藏字段

但是,我注意到没有人回答您问题的这一部分:

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

在这种情况下,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;

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

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

看不到实例变量,而是看到局部变量。永远不会设置实例变量,因为局部范围的变量在其作用域生存期内隐藏了实例变量。

从规格:

3.7.1 名称隐藏

实体

的作用域通常包含比实体的声明空间更多的程序文本。特别实体的范围可能包括引入新的声明包含同名实体的声明空间。这样声明会导致原始实体变为隐藏状态。相反当实体未隐藏时,它被称为可见的。

名称隐藏当作用域通过嵌套重叠以及作用域重叠时发生通过继承。两种隐藏方式的特点将在以下各节中介绍。

嵌套

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

在每个方法中设置一个断点。当局部变量, testVar,是在 OnInit 中设置的,如果我快速观察实例变量,它会 还具有"测试"值。当我玩到Page_Load时, 实例变量的值为 null。

问题是,VisualStudio的手表基于断点提示时刻变量的命名。因此,当在方法中命中断点时OnInit即使调试器显示实例变量具有值,这也是错误的数据可视化

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