附加事件处理程序仅被调用一次 [英] Attach event handler to be called only once

查看:243
本文介绍了附加事件处理程序仅被调用一次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在尝试编写扩展功能,以便能够轻松附加仅在触发事件后才取消订阅一次的动作.

I am currently trying to write an extension function to be able to easily attach an action that is only used once when the event is fired, then unsubscribed.

我正在尝试这样的事情:

I am trying something like this:

public static void AttachOnce<TEventArgs>([NotNull] this EventHandler<TEventArgs> me, [NotNull] Action<object, TEventArgs> action)
    where TEventArgs : System.EventArgs
{
    var handler = me;
    EventHandler<TEventArgs> wrappedAction = null;
    wrappedAction = (sender, args) =>
    {
        action(sender, args);
        handler -= wrappedAction;
    };
    handler += wrappedAction;
}

但是ReSharper抱怨取消订阅handler访问修改后的封包" .

But ReSharper complains on the unsubscribe that handler is "Access to modified closure".

我知道这意味着什么,所以我已经为闭包创建了局部变量,但似乎无法解决它.这里出了什么问题?

I know what this means, so I made the local variable for the closure already, but it doesn't seem to resolve it. What is failing here?

直接的硬编码代码有效.像这样:

The direct hard-coded code works. Something like this:

var file = new FileSystemWatcher("path/to/file");

FileSystemEventHandler handler = null;
handler = (sender, args) =>
{
    // My action code here
    file.Changed -= handler;
};
file.Changed += handler;

编辑1(2018-10-09 11:43 CET):

我可能太快了,在彻底考虑之前先问一个问题.
您无法在事件上创建扩展方法.完全没有.在C#中是不可能的.因此,我什至无法测试ReSharper为何抱怨,是否正确,因为file.Changed.AttachOnce(action)之类的调用无效.它说事件'Changed'只能出现在+ =或-=" 的左侧.

EDIT 1 (2018-10-09 11:43 CET):

I may just have been too fast, asking a question before thoroughly thinking it through.
You can't create extension methods on Events. At all. It's just not possible in C#. So I can't even test why ReSharper is complaining and if it is right, because a call like file.Changed.AttachOnce(action) is not valid. It says "The event 'Changed' can only appear on the left hand side of += or -=".

我发现了更多类似请求/问题的来源:
http://www.hardkoded.com/blog/csharp-wishlist-事件扩展
一次通用事件调用?

I have found some more sources for similar requests/questions:
http://www.hardkoded.com/blog/csharp-wishlist-extension-for-events
One time generic event call?

推荐答案

我一直在考虑一种不同但更简单的方法,即使用自我分离"内联处理程序,该处理程序将像这样使用:

I've been thinking about a different but much simpler approach, using a "self-detaching" inline handler which would be used like this:

obj.Event += (s, e) =>
{
    Detach(obj, nameof(obj.Event));
    // ..do stuff..
};

Detach方法看起来像这样,可以放在您喜欢的任何位置(很可能是静态帮助器类):

The Detach method would look like this and could be put anywhere you like (most likely a static helper class):

public static void Detach(object obj, string eventName)
{
    var caller = new StackTrace().GetFrame(1).GetMethod();
    var type = obj.GetType();
    foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic))
    {
        if (typeof(Delegate).IsAssignableFrom(field.FieldType))
        {
            var handler = (field.GetValue(obj) as Delegate)?.GetInvocationList().FirstOrDefault(m => m.Method.Equals(caller));
            if (handler != null)
            {
                type.GetEvent(eventName).RemoveEventHandler(obj, handler);
                return;
            }
        }
    }
}

因此对于您的示例,代码如下所示:

So for your example the code would look like this:

file.Changed += (s, e) =>
{
    Detach(file, nameof(file.Changed));
    // My action code here
};

这篇关于附加事件处理程序仅被调用一次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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