首页 > 解决方案 > 为什么表单关闭并清空后我仍然可以访问表单属性?

问题描述

我有一个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#winforms

解决方案


对于 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# 中的引用语义有透彻和深入的理解之前,不应使用此功能


推荐阅读