【问题标题】:Why can I still access form property after form is closed and nulled?为什么表单关闭并清空后我仍然可以访问表单属性?
【发布时间】:2019-02-21 08:59:30
【问题描述】:

我有一个MainForm 和一个UserConfigForm 并使用this 中的模式回答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,所以我决定检查事件处理程序的处理顺序是谨慎的。

似乎事件处理程序是按照它们注册的顺序处理的。所以在delegate { openForm = null }之后我可以访问userCfgForm.UserHasSaved是没有意义的。

我应该为此担心还是只为它有效?

【问题讨论】:

  • userCfgFormOpenEditFormClick 方法中本地定义时,我看不到如何从 ConfigFormClosed 方法中访问它。该变量是否有其他同名声明?
  • 对不起,我编辑了。我还在MainForm本地定义了一个UserConfigForm
  • 我不明白你为什么认为它的行为很奇怪。您已将相同的信息写入两个位置。你已经删除了其中一个。假设您要去度假,并在两个地方写下您的酒店房间号。当您退房时,您会扔掉其中一张纸。另一个上面还写着你的酒店房间号。它们是分开的纸片。你能解释一下为什么你认为清空一个变量会对另一个变量产生影响吗?
  • 我认为您可能对将变量设置为 null 的含义有一个非常错误的想法。将变量设置为 null 的效果是:该变量现在的值为 null。如果您认为将变量设置为 null 会以某种方式破坏该变量曾经引用的对象,那么您对 ​​C# 的工作原理有一个非常错误的想法,您应该了解它的实际工作原理。 这是你应该担心的事情
  • 不,这根本不对。 userCfgFormopenForm 都引用同一个对象。它们不相互引用了解其中的区别是绝对重要的,如果你不明白这一点,你将无法成功地进行 C# 编程。 C#确实 允许您使用ref 关键字将一个变量设为另一个变量的别名,但在您了解引用是什么之前,您不应尝试这样做。 引用就是值.

标签: c# winforms


【解决方案1】:

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

【讨论】:

  • 有道理。谢谢。
猜你喜欢
  • 2019-06-12
  • 2018-11-13
  • 2018-12-10
  • 1970-01-01
  • 2014-12-18
  • 2010-10-08
  • 2013-04-12
  • 2021-06-09
  • 2012-12-09
相关资源
最近更新 更多