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中获取赋值,但在退出该方法时立即丢失它。
有人可以向我解释这种行为吗?
答案 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 变量具有值,这也是数据的错误可视化。
所以一切都很好,只有数据的可视化不正确。