.net 使用IDisposable取消订阅事件

.net 使用IDisposable取消订阅事件,.net,events,idisposable,unsubscribe,.net,Events,Idisposable,Unsubscribe,我有一个处理WinForms控件事件的类。根据用户正在做的事情,我将推迟类的一个实例,并创建一个新实例来处理相同的事件。我需要先从活动中取消订阅旧实例-这很简单。如果可能的话,我想以非专有的方式来做这件事,这似乎是IDisposable的工作。但是,大多数文档建议仅在使用非托管资源时才使用IDisposable,此处不适用 如果我在Dispose()中实现IDisposable并取消订阅事件,我是否违背了它的意图?我应该提供一个Unsubscribe()函数并调用它吗 编辑:这里有一些虚拟代码

我有一个处理WinForms控件事件的类。根据用户正在做的事情,我将推迟类的一个实例,并创建一个新实例来处理相同的事件。我需要先从活动中取消订阅旧实例-这很简单。如果可能的话,我想以非专有的方式来做这件事,这似乎是IDisposable的工作。但是,大多数文档建议仅在使用非托管资源时才使用IDisposable,此处不适用

如果我在Dispose()中实现IDisposable并取消订阅事件,我是否违背了它的意图?我应该提供一个Unsubscribe()函数并调用它吗


编辑:这里有一些虚拟代码,可以显示我在做什么(使用IDisposable)。我的实际实现与一些专有数据绑定有关(说来话长)

在实际代码中,“EventListener”类更为复杂,每个实例都具有唯一的重要性。我在一个集合中使用它们,并在用户点击时创建/销毁它们


结论

我接受了,至少现在是这样。我觉得在不涉及非托管代码的情况下(这个对象的用户怎么会知道这一点呢?),使用熟悉的界面的好处超过了使用它的任何可能的缺点


如果有人不同意,请发表/评论/编辑。如果有更好的理由反对IDisposable,那么我将更改已接受的答案。

我个人的投票是使用Unsubscribe方法,以便将该类从事件中删除。IDisposable是一种用于非托管资源的确定性释放的模式。在这种情况下,您不需要管理任何非托管资源,因此不应该实现IDisposable


IDisposable可以用于管理事件订阅,但可能不应该。举个例子,我让你看看WPF。这是一个充满事件和事件处理程序的库。然而,WPF中几乎没有一个类实现IDisposable。我认为这表明事件应该以另一种方式处理

IDisposable坚定地关注资源,我认为这是足够多的问题的根源,不会使水进一步变得浑浊


我也投票赞成在你自己的界面上取消订阅的方法。

是的,去吧。尽管有些人认为IDisposable只针对非托管资源实现,但事实并非如此——非托管资源恰好是实现它的最大优势,也是最明显的原因。我认为它获得了这个想法,因为人们想不出任何其他理由来使用它。它不像决赛选手,这是一个性能问题,GC不容易处理好

在dispose方法中放入任何整理代码。它将更清晰、更干净,并且更可能防止内存泄漏,并且比试图记住取消引用更容易正确使用

IDisposable的目的是让您的代码工作得更好,而不必做大量的手工工作。利用它的力量来帮助你,克服一些人为的“设计意图”的胡说八道


我记得当.NET刚问世时,要说服微软相信确定性最终定稿的有用性是非常困难的——我们赢得了这场战斗,并说服他们添加了它(即使当时它只是一种设计模式),使用它吧

一个选择可能是根本不取消订阅-只是改变订阅的含义。如果事件处理程序可以变得足够智能,能够根据上下文知道它要做什么,那么您首先就不需要取消订阅


在您的特定情况下,这可能是一个好主意,也可能不是一个好主意-我认为我们没有足够的信息-但值得考虑。

另一种选择是使用或类似的方式,而不是明确取消订阅


S.[OT]我认为只提供强有力的委托人是.NET平台上最昂贵的设计错误。

< P>我认为一次性是用于GC不能自动处理的任何事情,而事件引用在我的书中计数。这是我想出的一个助手类

public class DisposableEvent<T> : IDisposable
    {

        EventHandler<EventArgs<T>> Target { get; set; }
        public T Args { get; set; }
        bool fired = false;

        public DisposableEvent(EventHandler<EventArgs<T>> target)
        {
            Target = target;
            Target += new EventHandler<EventArgs<T>>(subscriber);
        }

        public bool Wait(int howLongSeconds)
        {
            DateTime start = DateTime.Now;
            while (!fired && (DateTime.Now - start).TotalSeconds < howLongSeconds)
            {
                Thread.Sleep(100);
            }
            return fired;
        }

        void subscriber(object sender, EventArgs<T> e)
        {
            Args = e.Value;
            fired = true;
        }

        public void Dispose()
        {
            Target -= subscriber;
            Target = null;
        }

    }
公共类可处置事件:IDisposable
{
EventHandler目标{get;set;}
公共T参数{get;set;}
bool fired=false;
公共可处置事件(EventHandler目标)
{
目标=目标;
Target+=新事件处理程序(订户);
}
公共布尔等待(整数秒)
{
DateTime start=DateTime.Now;
同时(!fired&&(DateTime.Now-start).TotalSeconds
这使您可以编写以下代码:

Class1 class1 = new Class1();
            using (var x = new DisposableEvent<object>(class1.Test))
            {
                if (x.Wait(30))
                {
                    var result = x.Args;
                }
            }
Class1=newclass1();
使用(var x=新的可处置事件(class1.测试))
{
如果(x.等待(30))
{
var结果=x.Args;
}
}

一个副作用是,您不能在事件上使用event关键字,因为这会阻止将它们作为参数传递给helper构造函数,但是,这似乎没有任何不良影响。

使用
IDisposable
模式取消订阅事件时,有一个困扰我的问题是最终确定问题

IDisposable
中的
Dispose()
函数应该由开发人员调用,但是,如果开发人员没有调用它,则可以理解GC将调用此函数(至少通过标准的
IDisposable
模式)。但是,在您的情况下,如果您不调用
Dispose
其他人也不会
Class1 class1 = new Class1();
            using (var x = new DisposableEvent<object>(class1.Test))
            {
                if (x.Wait(30))
                {
                    var result = x.Args;
                }
            }