匿名委托事件侦听器是否会阻止垃圾收集

本文关键字:是否 事件 侦听器 | 更新日期: 2023-09-27 17:49:18

我不确定在以下场景中是否能够垃圾收集子窗口。

  1. 用户控件包含"显示弹出窗口"命令
  2. 该命令创建一个子窗口,并为"Closed"事件添加一个匿名侦听器

public partial class MainPage : UserControl
{
    public ICommand PopupCommand { get; private set; }
    public MainPage()
    {
        InitializeComponent();
        PopupCommand = new DelegateCommand(arg => 
        {
            var child = new ChildWindow();
            child.Closed += (sender, args) =>
            {
                MessageBox.Show("You closed the window!");
            };
            child.Show();
        });
    }
}

由于PopupCommand的委托表面上仍然包含对本地child变量的引用,因此每次调用PopupCommand都会泄漏内存吗?或者垃圾收集器会以某种方式识别出它可以在child关闭后处理它吗?


相关:从C#和垃圾收集中的事件分离匿名侦听器

匿名委托事件侦听器是否会阻止垃圾收集

以下测试表明,不,该场景不会导致内存泄漏。

public partial class LeakTest : UserControl
{
    public ICommand PopupCommand { get; private set; }
    public LeakTest()
    {
        InitializeComponent();
        PopupCommand = new DelegateCommand(arg =>
        {
            var child = new ChildWindow();
            child.Closed += (sender, args) =>
            {
                System.Diagnostics.Debug.WriteLine("Closed window");
            };
            // when the window has loaded, close it and re-trigger the command
            child.Loaded += (sender, args) =>
            {
                child.Close();
                PopupCommand.Execute(null);
            };
            child.Show();
        });
    }
}

原因在Jwosty链接的(Winforms(帖子的回复中提出:

在您的示例中,发布者只存在于私有方法的范围内,因此在方法返回后的某个时刻,对话框和处理程序都将被垃圾收集。

换句话说,内存泄漏问题实际上是相反的——事件发布者(ChildWindow控件(持有对订阅者(DelegateCommand(的引用,但不是相反的。因此,一旦ChildWindow关闭,垃圾收集器就会释放其内存。