在表单关闭和无效后,为什么还可以访问表单属性?

skw*_*ear -2 c# winforms

我有一个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;
}
Run Code Online (Sandbox Code Playgroud)

在里面UserConfigForm我也有一个自动财产UserHasSaved

public bool UserHasSaved { get; private set; }
Run Code Online (Sandbox Code Playgroud)

现在在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
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是这有效,但我不明白为什么会这样.我注册了两个事件处理程序,FormClosed所以我决定检查处理事件处理程序的顺序是谨慎的.

事件处理程序似乎按照它们的注册顺序进行处理.所以它不会使我可以访问的意义userCfgForm.UserHasSaved之后delegate { openForm = null }.

我应该担心这个还是只是对它的工作感到高兴?

Eri*_*ert 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);
  }
}
Run Code Online (Sandbox Code Playgroud)

假设我们在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
  }
}
Run Code Online (Sandbox Code Playgroud)

怎么了?一样.

  • 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
  }
}
Run Code Online (Sandbox Code Playgroud)

ref方法本地变量y的别名变量F1.X,所以当我们改变F2.x,我们改变y还,因为他们只是两个名称同一个变量 ; 他们是别名.

请注意,变量的别名与变量的类型无关.您可以为int变量创建别名,您可以为对象变量创建别名,无论如何.别名变量和别名本地必须具有完全相同的类型(练习:为什么?)但该类型可以是您想要的任何类型.

但规则是:别名变量可以是任何变量; 别名变量只能是本地或形式参数.例如,没有办法制作"ref int"字段.请注意,这ref int y是一个本地,而不是一个字段.

返回变量别名是一个高级功能,我们多年来一直争论是否适合添加到C#.在对C#中的引用语义进行全面深入理解之前,不应使用此功能.