是否有必要在C#中显式删除事件处理程序

我有一门课提供几个活动。 该类是全局声明的,但没有声明该全局声明 - 它在需要它的方法中根据需要进行实例化。

每次在方法中需要该类时,它都会实例化并注册事件处理程序。 在方法超出范围之前,是否有必要显式移除事件处理程序?

当方法超出范围时,类的实例也会发生。 离开事件处理程序注册到超出范围的实例是否具有内存占用量的含义? (我想知道事件处理程序是否让GC看到不再被引用的类实例。)

谢谢。


在你的情况下,一切都很好。 这是发布保持事件处理程序目标的事件的对象。 所以如果我有:

publisher.SomeEvent += target.DoSomething;

那么publisher提及target而不是相反。

在你的情况下,发布者将有资格进行垃圾回收(假设没有其他引用),因此事件处理程序目标的引用是无关紧要的。

棘手的情况是,发布者是长期存在的,但订阅者不想成为 - 在这种情况下,您需要取消订阅处理程序。 例如,假设您有一些数据传输服务,可以让您订阅有关带宽更改的异步通知,并且传输服务对象的使用寿命很长。 如果我们这样做:

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寿命至少与传输服务一样长。

就我个人而言,我很少遇到这种情况 - 通常如果我订阅一个事件,则该事件的目标至少与发布者一样长 - 例如,一个表单将持续与其上的按钮一样长。 值得了解这个潜在的问题,但我认为有些人不需要担心,因为他们不知道参考文献的位置。

编辑:这是为了回答Jonathan Dickinson的评论。 首先,查看Delegate.Equals(object)的文档,明确地给出了平等行为。

其次,这是一个简短但完整的程序来显示取消订阅工作:

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上测试)

进一步编辑:

这是为了证明在仍然有用户引用的情况下可以收集事件发布者。

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中,单声道在这里显得有点奇怪,稍后会研究一下):

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

在你的情况下,你很好。 我最初是在向后看你的问题,一个订阅者超出了范围,而不是出版商。 如果事件发布者超出了范围,那么对订阅者的引用(当然不是订阅者本身!)随之而去,并且不需要明确地删除它们。

我的原始答案如下,关于如果您创建事件订阅者并在不取消订阅的情况下让它超出范围会发生什么情况。 它不适用于你的问题,但我会留下它的历史。

如果该类仍然通过事件处理程序注册,则它仍然可以访问。 它仍然是一个活的对象。 跟随事件图形的GC会发现它已连接。 是的,你会想明确地删除事件处理程序。

仅仅因为对象超出了原始分配的范围,并不意味着它是GC的候选对象。 只要现场参考依然存在,它就是现场直播。

链接地址: http://www.djcxy.com/p/51511.html

上一篇: Is it necessary to explicitly remove event handlers in C#

下一篇: Is this object