【问题标题】:When can the GC collect a Lambda that was hooked to an Event?GC 什么时候可以收集到与 Event 挂钩的 Lambda?
【发布时间】:2015-09-10 06:24:54
【问题描述】:

如果我将 Lambda 挂钩到这样的事件:

static void DoSomething(Control control)
{
    control.SomeEvent += StaticMethodThatHandlesEvent;
    Control parentControl = control.Parent;
    parentControl.Disposed += (sender, args) =>
        {
            control.SomeEvent -= StaticMethodThatHandlesEvent;
        };
}

在什么条件下可以收集到 lambda?如果收集到 parentControl 会被收集吗?是否可以收集 parentControl(假设它已正确处理并且我的代码中没有对它的任何引用)?

编辑:整个代码在一个静态类中。这相关吗?

【问题讨论】:

  • 这取决于匿名委托的实现方式。特别是,lambda 的主体中是否有任何闭包?
  • 我解决了这个问题,现在它与我所做的非常接近。所以我想我在匿名代表中只有control
  • 那不是多余的吗?当然,如果父控件被收集,子控件也会被收集,包括事件处理程序委托。你到底想解决什么?只有当事件的存活时间超过其处理程序时,事件处理程序才会真正“泄漏”——Control 几乎不是这种情况。
  • MethodThatHandlesEvent 是一个静态方法。没有对事件处理程序所挂钩的 Object 的引用吗?

标签: c# .net lambda garbage-collection


【解决方案1】:

当您有这样的问题时,请尝试在不使用匿名方法的情况下执行此操作,并查看它需要什么才能工作:

internal class Program {
    private static void Main(string[] args) {
        DoSomething(new Control() {Parent = new Control()});
    }

    private static void DoSomething(Control control) {
        control.SomeEvent += MethodThatHandlesEvent;
        Control parentControl = control.Parent;
        parentControl.Disposed += new LambdaClass(control).OnDisposed;
    }

    private class LambdaClass {
        private readonly Control _control;
        public LambdaClass(Control control) {
            _control = control;
        }

        public void OnDisposed(object sender, EventArgs e) {
            // if MethodThatHandlerEvent is not static, you also need to pass and store reference to the wrapping class
            _control.SomeEvent -= MethodThatHandlesEvent;
        }
    }


    private static void MethodThatHandlesEvent(object sender, EventArgs e) {

    }

    private class Control {
        public event EventHandler SomeEvent;
        public event EventHandler Disposed;
        public Control Parent { get; set; }
    }
}

现在你有同样的情况,但没有任何匿名方法。问您的问题 - control 和 parentControl 已经相互引用,因此从 parentControl 添加一个间接引用到 control(通过 LambdaClass)不会改变情况。 parentControl 和 control(以及 LambdaClass 的实例)都应该由 GC 收集,当没有其他从根(本地、静态等)对它们的引用时。 .NET GC 收集循环引用没有问题。

【讨论】:

    【解决方案2】:

    你帮的太多了。 lambda 需要一个“显示类”,这是捕获对 control 的引用所必需的。稍后用于取消订阅事件。父控件通过其 Dispose 事件具有对显示类对象的引用,而显示类具有对 control 的引用。

    所以现在 control 和 lambda 的显示类对象都不能被收集,直到可以收集父控件。当您删除 lambda 时,可以更快地收集 control,而不管父级的生命周期如何。这就是你想要的,子控件总是在它们的父控件之前死掉。

    取消订阅 SomeEvent 只是没有必要,该事件不能让任何其他对象保持活动状态。由于 MethodThatHandlesEvent() 必须 是静态方法,因此这种情况经常发生,但在这里非常清楚。必要,因为 DoSomething() 是静态的。

    只需删除 lambda。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多