附加事件处理程序仅被调用一次 [英] Attach event handler to be called only once
问题描述
我目前正在尝试编写扩展功能,以便能够轻松附加仅在触发事件后才取消订阅一次的动作.
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屋!