在表单关闭并为空后,为什么仍可以访问表单属性?

时间:2019-02-20 18:49:29

标签: c# winforms

我有一个MainForm和一个UserConfigForm,并将Can I escape html special chars in javascript?答案中的模式用于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 }

我应该为此担心还是高兴吗?

1 个答案:

答案 0 :(得分:8)

对于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 local ,而不是 field

返回变量别名是一项高级功能,我们多年来一直在争论是否应将其添加到C#中。 除非您对C#中的引用语义有透彻和深入的了解,否则不要使用此功能。