c# - 为什么表单关闭并清空后我仍然可以访问表单属性?
问题描述
我有一个MainForm
和一个UserConfigForm
,并使用了这个答案中的模式,UserConfigForm
即
private static UserConfigForm openForm = null;
public static UserConfigForm GetInstance()
{
if (openForm == null)
{
openForm = new UserConfigForm();
openForm.FormClosed += delegate { openForm = null; };
}
return openForm;
}
在里面UserConfigForm
我也有一个自动属性,UserHasSaved
即
public bool UserHasSaved { get; private set; }
现在在MainForm
我需要检查配置表单关闭时是否必须重新加载用户配置。所以在MainForm
我有,
private UserConfigForm userCfgForm;
private void OpenEditFormClick(object sender, EventArgs e)
{
userCfgForm = UserConfigForm.GetInstance();
userCfgForm.FormClosed += ConfigFormClosed;
userCfgForm.Show();
{
private void ConfigFormClosed(object sender, FormClosedEventArgs e)
{
if (userCfgForm.UserHasSaved)
{
MessageBox.Show(message, caption);
//Reload config
}
}
问题是这行得通,但我不明白为什么会这样。我注册了两个事件处理程序,FormClosed
所以我决定检查事件处理程序的处理顺序是谨慎的。
似乎事件处理程序是按照它们注册的顺序处理的。所以我可以userCfgForm.UserHasSaved
在之后访问没有意义delegate { openForm = null }
。
我应该担心这个还是只为它的工作感到高兴?
解决方案
对于 C# 中引用类型的变量如何工作,您有一个常见的初学者误解。
让我们看一个更简单的例子:
class F1
{
static int x = 0;
public static int Start()
{
x = 1;
return x;
}
public static void Stop()
{
x = 0;
}
}
class F2
{
int y;
void DoIt()
{
this.y = F1.Start();
F1.Stop();
Console.WriteLine(this.y);
}
}
假设我们在 F2 的实例上调用 DoIt。的价值是this.y
多少?
跟踪程序的动作:
- F1.x 从零开始。
- F2.DoIt 调用 F1.Start()
- F1.x 变为 1
- F1.Start() 返回 1
- F2.y 变为 1
- F2.DoIt 调用 F1.Stop()
- F1.x 变为 0
F2.y 仍然是 1。更改 F2.x 并没有改变 F2.y 的任何内容。这是一个完全不同的变量。我们没有创建任何类型的神奇连接,即“当您读取 F2.y 时,真正读取 F2.x 的当前值”。
在您的程序中也是如此。我们可以将其更改为引用类型,并且没有任何变化:
class F1
{
public static F1 x = null;
public static F1 Start()
{
x = new F1();
return x;
}
public static void Stop()
{
x = null;
}
}
class F2
{
F1 y;
void DoIt()
{
this.y = F1.Start();
F1.Stop();
Console.WriteLine(this.y == null); // false
}
}
怎么了?一样。
- F1.x 以 null 开头。
- F2.DoIt 调用 F1.Start()
- F1.x 成为对有效对象的引用
- F1.Start() 返回对有效对象的引用
- F2.y 成为对有效对象的引用
- F2.DoIt 调用 F1.Stop()
- F1.x 变为空
F2.y 是什么? 仍然是对有效对象的引用。F2.y从来不是对 F1.x 的引用。它们都是对同一个有效对象的引用。引用是值,就像整数一样。
现在,如果您想为变量创建别名,C# 7 允许您这样做:
class F1
{
static int x = 0;
public static ref int Start()
{
x = 1;
return ref x;
}
public static void Stop()
{
x = 0;
}
}
class F2
{
void DoIt()
{
ref int y = ref F1.Start();
F1.Stop();
Console.WriteLine(y); // 0
}
}
这ref
意味着局部变量 y 是变量 F1.x 的别名,所以当我们更改时F2.x
,我们y
也会更改,因为它们只是同一个变量的两个名称;他们是别名。
请注意,变量的别名与变量的类型无关。您可以为 int 变量创建别名,也可以为对象变量创建别名,等等。别名变量和别名本地必须具有完全相同的类型(练习:为什么?)但该类型可以是您想要的任何类型。
但规则是:别名变量可以是任何变量;别名变量只能是局部或形式参数。例如,没有办法创建“ref int”字段。请注意,这ref int y
是一个本地,而不是一个字段。
返回变量别名是一项高级功能,我们多年来一直在争论将其添加到 C# 中是否正确。在对 C# 中的引用语义有透彻和深入的理解之前,不应使用此功能。
推荐阅读
- javascript - Java Script getJSON 在 laravel 5.+ 中不能正常工作?
- maven - 为什么 Hadoop 3.1.0 中删除了 DFSInotifyEventInputStream?
- bash - 如何从 Bash 历史文件中删除重复的命令?
- c# - 在该事件中设置 Checked 后,如何防止 CheckChanged 触发?
- iis-8 - IIS 工作进程重叠
- swift - 结构不在新对象上调用属性观察者
- node.js - Firebase Cloud Functions - 在 Firestore 中移动数据
- c# - 如果没有从 DbContext 类派生的上下文类,则无法使用启用迁移创建默认数据库
- php - 两次跨域
- python - E1120: 构造函数调用中的参数没有值