【问题标题】:Local variable with same name as instance variable = unexpected results与实例变量同名的局部变量 = 意外结果
【发布时间】:2012-04-25 00:11:24
【问题描述】:

ASP.NET 4.0 网络表单项目。我的代码隐藏中有以下内容。

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

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

【问题讨论】:

  • 你是怎么看实例变量的?也许您正在查看局部变量?
  • 我认为您更有可能在调试器中查看局部变量而不是实例变量,或者调试器被窃听并将局部变量的值显示为实例变量。
  • ReSharper 会就这类事情发出警告,就像我的常识一样。了解行为后,请更改其中之一的名称:-)
  • 这纯粹是一个猜测,但我猜你看到的是调试器被混淆了。您是否尝试过插入 f.e.: String testVar2 = this.testVar;在 OnInit() 中?我的猜测是你仍然会看到 testVar2 为空。至于为什么会编译,有一个与实例变量同名的局部变量并不违法。

标签: c# instance-variables local-variables


【解决方案1】:

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

没有看到实例变量,你看到的是局部变量。永远不会设置实例变量,因为局部作用域变量在其作用域生命周期内隐藏了实例变量。

来自规范:

3.7.1 名称隐藏

实体的范围通常包含比实体的声明空间更多的程序文本。特别是, 实体的范围可能包括引入新的声明 包含同名实体的声明空间。 这样的 声明会导致原始实体隐藏。反过来, 实体在不隐藏时被称为可见。

名称隐藏 当范围通过嵌套重叠以及范围重叠时发生 通过继承。两种隐藏方式的特点 将在以下部分进行描述。

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

【讨论】:

  • 是的。如果您想查看成员变量,请在 Watch 窗口中输入 this.testVar
  • 这不是调试器错误吗?如果我将鼠标悬停在一个变量上,它不应该在鼠标下显示变量的值而不是显示局部变量的值吗? VS 2015 中的行为相同。
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

    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;
    

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

    【讨论】:

    • 很好的解释。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 2016-04-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多