与lambdas一起使用的弱事件处理程序模型 [英] Weak event handler model for use with lambdas

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

问题描述

OK,所以这比一个问题更多的是一个答案,但在询问这个问题,并将各个位从 Dustin Campbell Egor ,还有来自 IObservable / Rx / Reactive framework ',我想我已经为这个特定的问题。它可能被IObservable / Rx / Reactive框架完全取代,但只有经验才会显示。

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.

有很多相关的问题,大多数人告诉你,如果你想要使用内联的lambdas,可以稍后分离:

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

  • 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 事件和一个特定的 EventHandler< 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

(*这是从 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...)

注意:在某些情况下,这可以留下在内存中为lambdas创建的包装类的引用,但是它们只能重写字节,所以我不会太麻烦。

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);
}



细节



我的出发点是 Egor 非常好的答案(请参阅有评论的版本链接):

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;
}

令我困扰的是,该事件是硬编码的方法。所以这意味着每个新的事件,都有一种新的写法。

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);
}

然而,该解决方案的问题是它只是通用的,它可以' t处理标准的winform,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);
}

然而,正如我暗示这里,这不会编译,因为没有办法限制T作为代表。

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

在这一点上,我几乎放弃了。没有任何意图试图与C#规范作斗争。

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

然而,昨天,我从Reactive框架中发现了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.

我完全建议花时间了解反应框架(或任何最终被称为)。这是非常有趣的,轻微的头脑。我怀疑它也会使这样的问题完全是多余的。

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.

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

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