Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/303.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 不死形态仍在生成事件_C#_Winforms_Dispose - Fatal编程技术网

C# 不死形态仍在生成事件

C# 不死形态仍在生成事件,c#,winforms,dispose,C#,Winforms,Dispose,我有一个用户可以从其他表单打开的表单: private void btnEditTemplate_Click(object sender, EventArgs e) { using (frmReport EditReport = new frmReport()) { EditReport.ShowDialog(); EditReport.Close(); EditReport.Dispose(); }

我有一个用户可以从其他表单打开的表单:

  private void btnEditTemplate_Click(object sender, EventArgs e)
  {
     using (frmReport EditReport = new frmReport()) 
     {
        EditReport.ShowDialog();
        EditReport.Close(); 
        EditReport.Dispose();
     }
  }
如您所见,我调用了close、dispose,甚至将表单放置在using块中。表单有一些绑定到表单外部维护的静态绑定列表的控件,更改这些列表会触发事件

尽管如此,如果用户稍后打开此表单的另一个副本,我发现原始表单的事件仍在运行,即使它已关闭并被释放

我该怎么做才能杀死这东西?刺穿心脏


更新:正如注释中所指出的,问题在于静态BindingList对象正在维护与旧表单的数据绑定。对我来说,解决方案是摆脱BindingList对象,因为我没有(有意)使用它们的数据绑定属性。

一般来说,我发现编写事件订阅代码是一种很好的做法,可以在取消订阅事件后容忍被调用。这可能以多种方式发生

我不知道你做了什么实验,但举个例子,如果你写了以下内容:

private void btnEditTemplate_Click(object sender, EventArgs e)
{
  using (frmReport EditReport = new frmReport()) 
  {
    EditReport.ShowDialog();
    EditReport.Close(); 
    EditReport.Dispose();
  }
  using (frmReport EditReport2 = new frmReport()) 
  {
    EditReport2.ShowDialog();
    EditReport2.Close(); 
    EditReport2.Dispose();
  }
}
然后,在第一个对话框的消息队列中可能仍有一些事件,在第二个实例的ShowDialog()调用中进入模态消息循环之前,这些事件将不会得到处理


即使使用您拥有的Dispose(),垃圾收集器也不会回收第一个对象,因为在消息队列中的这些订阅中引用了第一个对象。如果您将事件订阅写入“不做任何事情”状态,并且它们所传递到的对象已被释放,则不会造成任何伤害。要检查的内容取决于您使用的基类。最糟糕的情况是,您实现了自己的IsDisposed或IsClosed实例变量。

忽略当前的事件处理程序和数据绑定,您的代码目前有点过分了。你可以就这样逃脱:

using (frmReport EditReport = new frmReport())
{
    EditReport.ShowDialog();
}
当它到达您的
.Close()
调用时,您将它显示为对话框,因此表单仍然关闭。而
using
语句为您调用
.Dispose()

现在,当您处理一个窗体时,基本上就是从其父窗体中删除该窗体(如果有),处理所有子控件,并释放Windows句柄

但是,它不知道如何分离事件处理程序,也不知道如何删除数据绑定。因为这些东西是对表单的引用,所以垃圾收集器会将表单作为活动对象计数,而不会尝试收集它

因此,如果表单中有任何处理程序监听外部事件并触发,那么表单将尝试处理它们

您需要确保在表单关闭时分离所有处理程序并取消绑定所有数据绑定


第二次打开表单与第一次没有直接关系,但很可能是它导致数据源开始引发事件或更新值,而这正是导致原始表单失败的原因。

是否可能取消订阅事件?取消绑定?@MeirionHughes-我想是的,但我真正的问题是,为什么不处理表单呢?处理只是为了清理GC永远不会清理的非托管资源。它可以用于一般清理,但最初用于非托管清理。最后,如果您没有取消订阅对象事件处理程序,那么该对象不是孤立的,GC无法释放它;因为订阅事件不是由垃圾收集器控制的。此外,如果您使用BindingList控件,则不必显式订阅任何事件。解决方案是删除您在Form_closing事件中添加的任何显式绑定。不过我明白你的问题,比如“为什么取消订阅的事件会导致内存泄漏?”让你有更多的控制权。我认为这里有一个线索:“静态绑定列表”。表单有必要取消订阅表单之外的所有事件和长期存在的对象。如果您不这样做,那么这些订阅将使您的对象保持在内存中,只要外部对象存在。我假设这不是你正在处理的问题。我的代码不是“过度杀戮”,因为过度杀戮意味着“杀戮”,我问这个问题的原因就是没有一个是杀戮。我把每一件事都放进去,以展示我在杀人尝试中的尝试和失败。由于问题的根源是维护旧表单数据绑定的静态绑定列表,因此我将此标记为答案。