是否有必要明确删除在C#中的事件处理程序 [英] Is it necessary to explicitly remove event handlers in C#

查看:197
本文介绍了是否有必要明确删除在C#中的事件处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类,提供了一些事件。这一类是全局声明但在全球声明没有实例化 - 它实例化一个需要的基础上在需要它的方法。

I have a class that offers up a few events. That class is declared globally but not instanced upon that global declaration--it's instanced on an as-needed basis in the methods that need it.

每个需要的方法上课的时候​​,它是实例化和事件处理程序注册。是否有必要明确移除事件处理方法超出范围过吗?

Each time that class is needed in a method, it is instanced and event handlers are registered. Is it necessary to remove the event handlers explicitly before the method goes out of scope?

在该方法超出范围,所以那张类的实例。是否与实例是走出去的范围有一个内存占用寓意注册离开事件处理程序? (我想知道如果事件处理保持GC从为不再被引用看到类的实例。)

When the method goes out of scope, so goes the instance of the class. Does leaving event handlers registered with that instance that is going out of scope have a memory footprint implication? (I'm wondering if the event handler keeps the GC from seeing the class instance as no longer being referenced.)

感谢您。

推荐答案

在你的情况,一切都很好。这其中的的对象发布的这使事件处理程序的的目标的的赛事直播。所以,如果我有:

In your case, everything is fine. It's the object which publishes the events which keeps the targets of the event handlers live. So if I have:

publisher.SomeEvent += target.DoSomething;

然后出版商目标而不是倒过来一个参考。

then publisher has a reference to target but not the other way round.

在你的情况时,发行商将有资格进行垃圾回收(假设有以任何其它引用),这样的事实,它有给事件处理目标的参考是无关紧要的。

In your case, the publisher is going to be eligible for garbage collection (assuming there are no other references to it) so the fact that it's got a reference to the event handler targets is irrelevant.

棘手的情况是,当发行者是长寿命的,但用户不希望是 - 在的的情况下,你需要退订处理。例如,假设你有一些数据传输服务,它允许用户订阅关于带宽变化异步通知,和传输服务对象是长期居住。如果我们这样做:

The tricky case is when the publisher is long-lived but the subscribers don't want to be - in that case you need to unsubscribe the handlers. For example, suppose you have some data transfer service which lets you subscribe to asynchronous notifications about bandwidth changes, and the transfer service object is long-lived. If we do this:

BandwidthUI ui = new BandwidthUI();
transferService.BandwidthChanged += ui.HandleBandwidthChange;
// Suppose this blocks until the transfer is complete
transferService.Transfer(source, destination);
// We now have to unsusbcribe from the event
transferService.BandwidthChanged -= ui.HandleBandwidthChange;

(你会真的想使用finally块,以确保您不漏事件处理程序)。如果我们不退订,那么 BandwidthUI 至少只要将生活作为传输服务。

(You'd actually want to use a finally block to make sure you don't leak the event handler.) If we didn't unsubscribe, then the BandwidthUI would live at least as long as the transfer service.

我个人很少碰到过这样的 - 通常,如果我订阅事件,该事件的目标居住至少只要出版商 - 形式将持续,只要按钮,这就是它,例如。这是值得了解这个潜在的问题,但我觉得有些人担心,当他们不需要的,因为他们不知道哪种方式轮引用去了。

Personally I rarely come across this - usually if I subscribe to an event, the target of that event lives at least as long as the publisher - a form will last as long as the button which is on it, for example. It's worth knowing about this potential issue, but I think some people worry about it when they needn't, because they don't know which way round the references go.

编辑:这是回答乔纳森狄金森的评论。首先,我们看看 Delegate.Equals的文档(对象)其中明确给予平等的行为。

This is to answer Jonathan Dickinson's comment. Firstly, look at the docs for Delegate.Equals(object) which clearly give the equality behaviour.

其次,这里有一个简短而完整的程序,以显示退订工作:

Secondly, here's a short but complete program to show unsubscription working:

using System;

public class Publisher
{
    public event EventHandler Foo;

    public void RaiseFoo()
    {
        Console.WriteLine("Raising Foo");
        EventHandler handler = Foo;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
        else
        {
            Console.WriteLine("No handlers");
        }
    }
}

public class Subscriber
{
    public void FooHandler(object sender, EventArgs e)
    {
        Console.WriteLine("Subscriber.FooHandler()");
    }
}

public class Test
{
    static void Main()
    {
         Publisher publisher = new Publisher();
         Subscriber subscriber = new Subscriber();
         publisher.Foo += subscriber.FooHandler;
         publisher.RaiseFoo();
         publisher.Foo -= subscriber.FooHandler;
         publisher.RaiseFoo();
    }
}

结果:

Raising Foo
Subscriber.FooHandler()
Raising Foo
No handlers

(测试在Mono和.NET 3.5SP1。)

(Tested on Mono and .NET 3.5SP1.)

进一步编辑:

这是证明,虽然仍然有一个用户引用的事件发布可以收集。

This is to prove that an event publisher can be collected while there are still references to a subscriber.

using System;

public class Publisher
{
    ~Publisher()
    {
        Console.WriteLine("~Publisher");
        Console.WriteLine("Foo==null ? {0}", Foo == null);
    }

    public event EventHandler Foo;
}

public class Subscriber
{
    ~Subscriber()
    {
        Console.WriteLine("~Subscriber");
    }

    public void FooHandler(object sender, EventArgs e) {}
}

public class Test
{
    static void Main()
    {
         Publisher publisher = new Publisher();
         Subscriber subscriber = new Subscriber();
         publisher.Foo += subscriber.FooHandler;

         Console.WriteLine("No more refs to publisher, "
             + "but subscriber is alive");
         GC.Collect();
         GC.WaitForPendingFinalizers();         

         Console.WriteLine("End of Main method. Subscriber is about to "
             + "become eligible for collection");
         GC.KeepAlive(subscriber);
    }
}

结果(.NET 3.5SP1;单似乎表现得有一点奇怪的是,这里将考虑一些时间。)

Results (in .NET 3.5SP1; Mono appears to behave slightly oddly here. Will look into that some time):

No more refs to publisher, but subscriber is alive
~Publisher
Foo==null ? False
End of Main method. Subscriber is about to become eligible for collection
~Subscriber

这篇关于是否有必要明确删除在C#中的事件处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆