c# - 附加事件处理程序只调用一次
问题描述
我目前正在尝试编写一个扩展函数,以便能够轻松附加一个仅在事件触发时使用一次,然后取消订阅的操作。
我正在尝试这样的事情:
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
是"Access to modified closure"。
我知道这意味着什么,所以我已经为闭包创建了局部变量,但它似乎并没有解决它。这里有什么失败?
直接硬编码代码有效。像这样的东西:
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' 只能出现在 += 或 -=" 的左侧。
我找到了更多类似请求/问题的来源:
http ://www.hardkoded.com/blog/csharp-wishlist-extension-for-events
一次通用事件调用?
解决方案
我一直在考虑一种不同但更简单的方法,使用“自分离”内联处理程序,其使用方式如下:
obj.Event += (s, e) =>
{
Detach(obj, nameof(obj.Event));
// ..do stuff..
};
该Detach
方法看起来像这样,可以放在任何你喜欢的地方(很可能是静态帮助类):
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;
}
}
}
}
因此,对于您的示例,代码如下所示:
file.Changed += (s, e) =>
{
Detach(file, nameof(file.Changed));
// My action code here
};
推荐阅读
- tinymce - TinyMCE 删除了一些标签。忽略设置
- r - 如何从R中北极上的连接点停止最近邻分析
- python - 使用 ipyvuetify 通过 Oulook 发送电子邮件
- java - 我如何把素数的总和放在第一位,然后是它下面的素数
- loops - 为什么无限循环不增加整数?
- r - R Shiny renderDataTable() 得到 ajax 和 404 错误
- python - 当我使用 PyGame 死亡时,除箭头键之外的所有键都不起作用
- javascript - 使用 .split 函数拆分电话号码
- git - 如何解决 Gerrit 上的“ANCESTOR OUTDATED”问题?
- python - 如何在 Python 3 中实现可迭代的消费多进程池