弱事件处理模型与lambda表达式使用 [英] Weak event handler model for use with lambdas

查看:236
本文介绍了弱事件处理模型与lambda表达式使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好了,所以这是一个多问题的答案,但询问后,<一个href="http://stackoverflow.com/questions/371109/garbage-collection-when-using-anonymous-delegates-for-event-handling">this问题,以及各种数据位从<一个汇集href="http://diditwith.net/CommentView,guid,aacdb8ae-7baa-4423-a953-c18c1c7940ab.aspx#commentstart">Dustin坎贝尔,<一个href="http://stackoverflow.com/questions/371109/garbage-collection-when-using-anonymous-delegates-for-event-handling/1447619#1447619">Egor,之一,也是从<最后的技巧href="http://channel9.msdn.com/shows/Going+Deep/Expert-to-Expert-Brian-Beckman-and-Erik-Meijer-Inside-the-NET-Reactive-Framework-Rx/">IObservable/Rx/Reactive框架',我想我已经制定了这方面的问题一个可行的解决方案。它可以通过的IObservable / RX /无功框架完全取代,但只有经验将证明。

OK, so this is more of an answer than a question, but after asking this question, and pulling together the various bits from Dustin Campbell, Egor, and also one last tip from the 'IObservable/Rx/Reactive framework', I think I've worked out a workable solution for this particular problem. It may be completely superseded by IObservable/Rx/Reactive framework, but only experience will show that.

我特意创建了一个新的问题,给我空间来解释我是如何走到这个解决方案,因为它可能不会立即明显。

I've deliberately created a new question, to give me space to explain how I got to this solution, as it may not be immediately obvious.

有许多相关的问题,多数告诉你,你不能使用内联lambda表达式,如果你想以后能够分离他们:

There are many related questions, most telling you you can't use inline lambdas if you want to be able to detach them later:

  • 在.net弱事件?
  • 与脱钩lambda表达式事件在C#
  • <一个href="http://stackoverflow.com/questions/16473/can-using-lambdas-as-event-handlers-cause-a-memory-leak">Can使用lambda表达式作为事件处理程序会导致内存泄漏?
  • <一个href="http://stackoverflow.com/questions/805829/how-to-unsubscribe-from-an-event-which-uses-a-lambda-ex$p$pssion">How从它使用一个lambda EX pression事件退订?
  • 退订匿名在C#中的方法
  • Weak events in .Net?
  • Unhooking events with lambdas in C#
  • Can using lambdas as event handlers cause a memory leak?
  • How to unsubscribe from an event which uses a lambda expression?
  • Unsubscribe anonymous method in C#

和它是真实的,如果的的希望以后能够分离它们,你需要保持一个引用您的lambda。但是,如果你只是想在事件处理上脱落,当你的用户超出范围,这个答案是给你的。

And it is true that if YOU want to be able to detach them later, you need to keep a reference to your lambda. However, if you just want the event handler to detach itself when your subscriber falls out of scope, this answer is for you.

推荐答案

(阅读全文如下,如果你想看到我是如何走到这个解决方案)

使用情况,给出一个香草的MouseDown 事件和特定的控制事件处理程序&LT; ValueEventArgs> ValueEvent 事件:

Usage, given a control with a vanilla MouseDown event, and a specific EventHandler<ValueEventArgs> ValueEvent event:

// for 'vanilla' events
SetAnyHandler<Subscriber, MouseEventHandler, MouseEventArgs>(
    h => (o,e) => h(o,e), //don't ask me, but it works*.
    h => control.MouseDown += h,
    h => control.MouseDown -= h,
    subscriber,
    (s, e) => s.DoSomething(e));  //**See note below

// for generic events
SetAnyHandler<Subscriber, ValueEventArgs>(
    h => control.ValueEvent += h,
    h => control.ValueEvent -= h,
    subscriber,
    (s, e) => s.DoSomething(e));  //**See note below

(*这是从<一个一个的解决方法href="http://social.msdn.microsoft.com/Forums/en-US/rx/thread/50d01578-9217-43d3-8ccb-e3f859040a68">Rx)

(**避免调用用户对象直接在这里(例如把subscriber.DoSomething(E),或调用DoSomething的(E)直接,如果我们的用户类的内部是很重要的。这样做有效地创建一个参考订户,这完全违背了对象......)

(** it is important to avoid invoking the subscriber object directly here (for instance putting subscriber.DoSomething(e), or invoking DoSomething(e) directly if we are inside the Subscriber class. Doing this effectively creates a reference to subscriber, which completely defeats the object...)

注意:在某些情况下,这可以离开引用的内存中的lambda表达式创建的包装类,但他们仅重字节,所以我不会太在意

Note: in some circumstances, this CAN leave references to the wrapping classes created for the lambdas in memory, but they only weigh bytes, so I'm not too bothered.

执行:

//This overload handles any type of EventHandler
public static void SetAnyHandler<S, TDelegate, TArgs>(
    Func<EventHandler<TArgs>, TDelegate> converter, 
    Action<TDelegate> add, Action<TDelegate> remove,
    S subscriber, Action<S, TArgs> action)
    where TArgs : EventArgs
    where TDelegate : class
    where S : class
{
    var subs_weak_ref = new WeakReference(subscriber);
    TDelegate handler = null;
    handler = converter(new EventHandler<TArgs>(
        (s, e) =>
        {
            var subs_strong_ref = subs_weak_ref.Target as S;
            if(subs_strong_ref != null)
            {
                action(subs_strong_ref, e);
            }
            else
            {
                remove(handler);
                handler = null;
            }
        }));
    add(handler);
}

// this overload is simplified for generic EventHandlers
public static void SetAnyHandler<S, TArgs>(
    Action<EventHandler<TArgs>> add, Action<EventHandler<TArgs>> remove,
    S subscriber, Action<S, TArgs> action)
    where TArgs : EventArgs
    where S : class
{
    SetAnyHandler<S, EventHandler<TArgs>, TArgs>(
        h => h, add, remove, subscriber, action);
}

细节

我的出发点是<一个href="http://stackoverflow.com/questions/371109/garbage-collection-when-using-anonymous-delegates-for-event-handling/1447619#1447619">Egor's出色答卷(见链接的版本注释):

The detail

My starting point was Egor's excellent answer (see link for version with comments):

public static void Link(Publisher publisher, Control subscriber) {
    var subscriber_weak_ref = new WeakReference(subscriber);
    EventHandler<ValueEventArgs<bool>> handler = null;
    handler = delegate(object sender, ValueEventArgs<bool> e) {
            var subscriber_strong_ref = subscriber_weak_ref.Target as Control;
            if (subscriber_strong_ref != null) subscriber_strong_ref.Enabled = e.Value;
            else {
                    ((Publisher)sender).EnabledChanged -= handler;
                    handler = null; 
            }
    };

    publisher.EnabledChanged += handler;
}

什么困扰我的是,该事件是很难codeD插入方法。因此,这意味着对于每一个新的事件,有一个新的方法来写。

What bothered me was that the event is hard coded into the method. So that means for each new event, there is a new method to write.

我摆弄周围,设法想出这个通用的解决方案:

I fiddled around and managed to come up with this generic solution:

private static void SetAnyGenericHandler<S, T>(
     Action<EventHandler<T>> add,     //to add event listener to publisher
     Action<EventHandler<T>> remove,  //to remove event listener from publisher
     S subscriber,                    //ref to subscriber (to pass to action)
     Action<S, T> action)             //called when event is raised
    where T : EventArgs
    where S : class
{
    var subscriber_weak_ref = new WeakReference(subscriber);
    EventHandler<T> handler = null;
    handler = delegate(object sender, T e)
    {
        var subscriber_strong_ref = subscriber_weak_ref.Target as S;
        if(subscriber_strong_ref != null)
        {
            Console.WriteLine("New event received by subscriber");
            action(subscriber_strong_ref, e);
        }
        else
        {
            remove(handler);
            handler = null;
        }
    };
    add(handler);
}

然而与该溶液中的问题是,它是唯一的通用的,它不能处理的标准的Winforms的MouseUp,的MouseDown等...

However the problem with that solution is that it is ONLY generic, it can't handle the standard winforms MouseUp, MouseDown, etc...

于是,我就让它即使更多通用:

So I tried to make it even more generic:

private static void SetAnyHandler<T, R>(
    Action<T> add,      //to add event listener to publisher
    Action<T> remove,   //to remove event listener from publisher
    Subscriber subscriber,  //ref to subscriber (to pass to action)
    Action<Subscriber, R> action) 
    where T : class
{
    var subscriber_weak_ref = new WeakReference(subscriber);
    T handler = null;
    handler = delegate(object sender, R e) //<-compiler doesn't like this line
    {
        var subscriber_strong_ref = subscriber_weak_ref.Target as Subscriber;
        if(subscriber_strong_ref != null)
        {
            action(subscriber_strong_ref, e);
        }
        else
        {
            remove(handler);
            handler = null;
        }
    };
    remove(handler);
}

不过,正如我暗示<一个href="http://stackoverflow.com/questions/371109/garbage-collection-when-using-anonymous-delegates-for-event-handling/1687255#1687255">here,这不会编译,因为没有办法约束T可被委托的。

However, as I hinted here, this won't compile, because there is no way of constraining T to be a delegate.

在这一点上,我pretty的多少放弃了。有没有一点想用C#规格战斗。

At that point, I pretty much gave up. There's no point trying to fight with the C# specs.

不过,昨日,笔者从反应框架发现Observable.FromEvent方式,我没有执行,但用法似乎稍微熟悉,也很有趣:

However, yesterday, I discovered the Observable.FromEvent method from the Reactive framework, I didn't have the implementation, but the usage seemed slightly familiar, and very interesting:

var mousedown = Observable.FromEvent<MouseEventHandler, MouseDownEventArgs>(
      h => new MouseEventHandler(h),
      h => control.MouseDown += h,
      h => control.MouseDown -= h);

这是引起我注意的第一个参数。这是解决方法没有委托类型约束。我们通过传递,这将创建委托功能利用它。

It was the first argument that caught my attention. This is the workaround for the absence of a delegate type constraint. We take of it by passing in the function which will create the delegate.

把这个一起给了我们这个答案的上方显示的解决方案。

Putting all this together gives us the solution shown at the top of this answer.

我彻底地建议采取的时间来了解反应框架(或不管它最终被调用)。这很有趣,微微mindblowing。我怀疑,这也将呈现这样的问题完全是多余的。

I thoroughly recommended taking the time to learn about the reactive framework (or whatever it ends up being called). It is VERY interesting, and slightly mindblowing. I suspect that it will also render questions like this totally redundant.

到目前为止,最有趣的东西,我已经看到了一直对 Channel9的上的视频。

So far, the most interesting stuff I've seen has been the videos on Channel9.

这篇关于弱事件处理模型与lambda表达式使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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