C# 防止因添加事件句柄而导致内存泄漏的预防措施
我将制作一个GUI,它将动态创建一组控件,并将事件分配给它们。我需要在运行时添加和删除这些控件。它将如下所示:C# 防止因添加事件句柄而导致内存泄漏的预防措施,c#,events,memory,memory-leaks,C#,Events,Memory,Memory Leaks,我将制作一个GUI,它将动态创建一组控件,并将事件分配给它们。我需要在运行时添加和删除这些控件。它将如下所示: FlowLayoutPanel.Controls.Clear(); << add new controls, assigning Click events with += >> 当您将寿命较短的事件使用者附加到寿命较长的事件生产者时,这将是一个主要的问题。如果他们有相似的生活,或者与我描述的相反,这不是问题 如果您确实担心这个问题,只需使用-=从事件中分离。这
FlowLayoutPanel.Controls.Clear();
<< add new controls, assigning Click events with += >>
当您将寿命较短的事件使用者附加到寿命较长的事件生产者时,这将是一个主要的问题。如果他们有相似的生活,或者与我描述的相反,这不是问题 如果您确实担心这个问题,只需使用-=从事件中分离。这将删除附件创建的引用,并有助于避免此类内存问题 编辑:由于评论有点长,我将在这里发布一些后续信息。当您附加到事件时,您所做的是在事件提供程序上挂起对您自己的引用。例如,如果您有一个包含StrikesMidnight事件的Clock类,并且您从一个名为Bedtime的类订阅了该事件,那么实际的就寝机制是:
Clock.StrikesMidnight+=this.HandleMidnight代码>是指您正在为自己分配时钟引用。这就好像时钟有一个对象属性,你说Clock.ObjectProperty=this代码>
因此,在Bedtime类是短期的并且超出范围的情况下,Bedtime会出现,在时钟上挂起对自身的引用,然后超出范围。问题是,时钟仍然有一个引用,所以即使它超出范围,垃圾收集器也不会收集就寝时间
这就是背景。在您的例子中,您正在创建一个标签并将自己的引用附加到它上(通过“MethodX”处理程序)。调用refresh时,清除标签列表(意味着它们超出范围)。它们超出了范围,并且通过MethodX处理程序引用了您的类,但那又怎样呢?它们具有引用并不阻止它们被GC’ed。没有人在代码中保留对它们的引用,因此GC将对它们执行其工作,并且您不会泄漏内存。只要您处理包含的表单,垃圾收集器就应该清理所有控件
订阅控件上的事件不会使控件保持活动状态,因为该控件具有对处理委托的引用;委托没有对控件的引用
事件订阅将阻止清理控件的情况是,控件中的某些代码订阅了它所包含的表单实例之外的事件。例如,如果自定义组合框订阅了静态类上的事件,以让控件知道何时应该更新其选项列表。如果自定义控件未解除此事件的锁定,则在应用程序期间,静态类事件寄存器将引用它。因为控件将有一个对其容器的引用(等等),所以整个表单可能会保留在内存中。在这种情况下,控件应该在其Dispose方法中取消对事件的冻结。订阅静态类或长期实例类上的事件时,应始终发出红旗,并在不再需要时显式取消订阅
在表单中,只要所有事件都连接到表单类或其作用域由表单实例控制的对象上的实例方法,那么当表单所根的对象图超出作用域时,控件将被清除。请注意,在不再需要表单之后,外部类不会保存对表单的引用。我的第一个建议是不要进行预优化。在你试图解决它之前,确保这是一个问题
关于Dispose():在垃圾收集对象之前,Dispose应仅用于释放非托管资源。有关更多信息,请参阅
为了防止事件处理程序保留对控件的引用,您需要让控件在创建时取消订阅它们订阅的所有事件处理程序。提示:如果要通过GUI添加事件处理程序,请在调用FlowLayoutPanel.Controls.Clear()之前调查自动生成的代码,查看需要取消订阅的内容。一种干净的(-er?)方法是创建您自己的控件,该控件继承所需的控件,并添加一个“Cleanup()”方法,该方法取消订阅任何已订阅的事件处理程序(您应该知道订阅了哪些事件处理程序,这可能是因为您编写了代码,也可能是为您生成的).但我不知道要分离什么事件!那我该怎么办?这与你用+=语句做的事情正好相反。每次将本地事件处理程序附加到某个协作者的事件时,请记住在不再需要知道引发的事件时使用-=分离。如果需要,可以在dispose()方法中执行此操作,但严格来说这不是必需的。是的,但我不知道要分离哪个方法。比如说,我有10种方法,其中一种是根据当时的情况,在不久前贴在20个标签中的第5个标签上的。现在我需要从flowlayoutpanel中删除所有标签。我如何知道我必须使用10个事件处理程序中的哪一个-=?你能发布一些示例代码来说明你在说什么吗?这可能会让我更容易做出具体的回应。所有的代码都会占用大量的空间,而且不会相关。试想一下:创建了20个标签,每个标签从名为Foo1..Foo10的10个事件处理程序列表中获得一个随机单击事件处理程序,每个标签都添加到flowlayoutpanel。然后,在2分钟内,我需要清除flowlayout面板并再次执行。如何确保前20个标签拥有的内存是属性GCed?我不能使用-=因为它们是随机附加的,并且我没有在内存中存储关于谁得到了什么的信息。换句话说,如果一个超出范围的对象的事件中包含另一个对象的方法,那么它将被disp
private void Refresh()
{
Label l;
Random rnd = new Random();
// What code should i add here to prevent memory leaks
_flowLP.Controls.Clear();
l = new Label();
l.Text = "1";
if (rnd.Next(3) == 0) l.Click += Method1;
if (rnd.Next(3) == 0) l.Click += Method2;
if (rnd.Next(3) == 0) l.Click += Method3;
_flowLP.Controls.Add(l);
l = new Label();
l.Text = "2";
if (rnd.Next(3) == 0) l.Click += Method1;
if (rnd.Next(3) == 0) l.Click += Method2;
if (rnd.Next(3) == 0) l.Click += Method3;
_flowLP.Controls.Add(l);
l = new Label();
l.Text = "3";
if (rnd.Next(3) == 0) l.Click += Method1;
if (rnd.Next(3) == 0) l.Click += Method2;
if (rnd.Next(3) == 0) l.Click += Method3;
_flowLP.Controls.Add(l);
l = new Label();
l.Text = "4";
if (rnd.Next(3) == 0) l.Click += Method1;
if (rnd.Next(3) == 0) l.Click += Method2;
if (rnd.Next(3) == 0) l.Click += Method3;
_flowLP.Controls.Add(l);
l = new Label();
l.Text = "5";
if (rnd.Next(3) == 0) l.Click += Method1;
if (rnd.Next(3) == 0) l.Click += Method2;
if (rnd.Next(3) == 0) l.Click += Method3;
_flowLP.Controls.Add(l);
l = new Label();
l.Text = "6";
if (rnd.Next(3) == 0) l.Click += Method1;
if (rnd.Next(3) == 0) l.Click += Method2;
if (rnd.Next(3) == 0) l.Click += Method3;
_flowLP.Controls.Add(l);
}