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

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

问题描述

好的,所以这更像是一个答案而不是一个问题,但是在询问 这个问题,并将达斯汀坎贝尔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.

有很多相关的问题,大多数告诉你如果你想稍后分离它们,你不能使用内联 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:

确实,如果希望以后能够分离它们,您需要保留对您的 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.

推荐答案

'The' answer

(如果你想了解我是如何得到这个解决方案的,请阅读下面的更多内容)

用法,给定带有普通 MouseDown 事件和特定 EventHandler 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),或者如果我们在 Subscriber 类中,则直接调用 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);
}

然而,该解决方案的问题在于它只是通用的,它无法处理标准的 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...

所以我试着让它通用:

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天全站免登陆