与实例变量同名的局部变量 = 意外结果
本文关键字:局部变量 意外 结果 实例 变量 | 更新日期: 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;
}
}
我在每个方法中设置了一个断点。 当局部变量 testVar
在 OnInit
中设置时,如果我快速观察实例变量,它也有值"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
即使调试器显示实例变量具有值,这也是错误的数据可视化。
所以一切都很好,只是数据的可视化不正确。