简体   繁体   English

我是否需要取消订阅局部变量的匿名事件处理程序?

[英]Do i have to unsubscribe from anonymous event handlers of local variables?

If I have a code that looks something like this: 如果我有一个看起来像这样的代码:

public void Foo()
{
    Bar bar = new Bar();

    bar.SomeEvent += (sender, e) =>
    {
        //Do something here
    };

    bar.DoSomeOtherThingAndRaiseSomeEvent();
}

Will bar be collected when the method runs out of the scope, or will I have to manually unsubscribe from the event to prevent a memory leak because of a reference to SomeEvent ? 方法超出范围时会收集bar ,还是我必须手动取消订阅该事件以防止由于引用SomeEvent而导致内存泄漏?

Your situation is fine; 您的情况还不错; the event subscriber will not prevent the publisher from being collected, but the opposite can happen. 事件订阅者不会阻止发布者被收集,但是可能发生相反的情况。

For example, 例如,

class Foo
{
    public event EventHandler FooEvent;

    void LeakMemory()
    {
        Bar bar = new Bar();

        bar.AttachEvent(this);
    }
}

class Bar
{
    void AttachEvent(Foo foo)
    {
        foo.FooEvent += (sender, e) => { };
    }
}

In this case, the instance of Bar created within LeakMemory can't be collected until either 在这种情况下,只有在LeakMemory情况下才能收集在LeakMemory创建的Bar的实例

  • The anonymous method represented by the lambda is removed from FooEvent 's invocation list 由lambda表示的匿名方法已从FooEvent的调用列表中删除
  • The instance of Foo to which it's attached can be collected 可以将其附加到的Foo实例进行收集

This is because the event (which is just some syntactic sugar over an ordinary delegate instance) holds onto a list of delegates to invoke when it's invoked, and each of these delegates has, in turn, a reference to the object that it's attached to (in this case, the instance of Bar ). 这是因为该事件(仅是普通delegate实例上的某种语法糖)保留在调用时将要调用的委托列表上,并且这些委托中的每一个又具有对其附加对象的引用(在这种情况下,为Bar的实例)。

Note that we're only talking about collection eligibility here. 请注意,我们在这里仅讨论集合资格 Just because it's eligible doesn't say anything about when (or even, really, if ) it will be collected, just that it can be. 正因为它的资格并没有说什么时候 (甚至,真的, 如果 ),它会被收集,只是它可以是任何东西。

Well, the object bar refers won't be automatically garbage collected immediately... it's just that the bar variable won't prevent it from being garbage collected. 好吧,对象bar引用不会立即被自动垃圾回收……只是bar变量不会阻止它被垃圾回收。

The event handler won't prevent the instance of Bar from being garbage collected either though - the "normal" problem is that an event handler keeps the subscriber of an event from being garbage collected (if it uses an instance method or captures "this" in an anonymous function). 事件处理程序也不会阻止Bar的实例被垃圾回收-“正常”问题是事件处理程序使事件的订阅者无法被垃圾回收(如果它使用实例方法或捕获“ this”)在匿名函数中)。 It doesn't usually affect the publisher being garbage collected. 通常不会影响发布者被垃圾回收。 Just remember that the publisher needs to keep a reference to all the subscribers - the subscriber doesn't need to remember what it's subscribed to, unless it explicitly wants to unsubscribe or use some other member later. 只需记住发布者需要保留对所有订阅者的引用-订阅者不需要记住其订阅的内容,除非订阅者明确希望以后取消订阅或使用其他成员。

Assuming nothing else is keeping your instance of Bar alive, your code should be fine. 假设没有其他因素可以使Bar实例保持活动状态,则您的代码应该没问题。

The above answers are correct; 以上答案是正确的; I just wanted to make a note. 我只是想做个笔记。 Anonymous delegates used as handlers can only be unsubscribed if you retain some other reference to the delegate/lambda. 仅当您保留对委托/ lambda的其他引用时,才能取消订阅用作处理程序的匿名委托。 This is because lambdas are "function literals", kind of like string literals, however unlike strings they are NOT compared semantically when determining equality: 这是因为lambda是“函数文字”,有点像字符串文字,但是与字符串不同,在确定相等性时不会在语义上对它们进行比较:

public event EventHandler MyEvent;

...

//adds a reference to this named method in the context of the current instance
MyEvent += Foo;

//Adds a reference to this anonymous function literal to MyEvent
MyEvent += (s,e) => Bar();

...

//The named method of the current instance will be the same reference
//as the named method.
MyEvent -= Foo;

//HOWEVER, even though this lambda is semantically equal to the anonymous handler, 
//it is a different function literal and therefore a different reference,
//which will not match the anonymous handler.
MyEvent -= (s,e) => Bar();

var hasNoHandlers = MyEvent == null; //false

//To successfully unsubscribe a lambda, you have to keep a reference handy:

EventHandler myHandler = (s,e) => Bar();

MyEvent += myHandler;

...

//the variable holds the same reference we added to the event earlier,
//so THIS call will remove the handler.
MyEvent -= myHandler;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM