我目前正在尝试编写扩展功能,以便能够轻松附加仅在触发事件后才取消订阅一次的动作。
我正在尝试这样的事情:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="xs xd"
version="2.0">
<xsl:output method="xml" version="1.0" encoding="utf-8" omit-xml-declaration="no" indent="no" />
<xsl:strip-space elements="*" />
<xsl:template match="/">
<clients>
<xsl:attribute name="xsi:noNamespaceSchemaLocation">setClients.xsd</xsl:attribute>
<xsl:attribute name="encryptedData">N</xsl:attribute>
<xsl:for-each select="xsi:pointOfSale">
<xsl:if test="@mode='UPDATE'">
<client>
<xsl:attribute name="cstclavep" select="id"/>
<address>
<STREETNAME><xsl:value-of select="xsi:info/xsi:address/xsi:streetName"/></STREETNAME>
</address>
</client>
</xsl:if>
</xsl:for-each>
</clients>
</xsl:template>
但是ReSharper抱怨取消订阅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;
}
是“访问修改后的封包” 。
我知道这意味着什么,所以我已经为闭包创建了局部变量,但是它似乎无法解决它。这里出了什么问题?
直接的硬编码代码有效。像这样:
handler
我可能太快了,在彻底考虑之前先问一个问题。
您无法在事件上创建扩展方法。完全没有在C#中是不可能的。因此,我什至无法测试ReSharper为何抱怨,是否正确,因为var file = new FileSystemWatcher("path/to/file");
FileSystemEventHandler handler = null;
handler = (sender, args) =>
{
// My action code here
file.Changed -= handler;
};
file.Changed += handler;
之类的呼叫无效。它说“事件'Changed'只能出现在+ =或-=” 的左侧。
我发现了更多类似请求/问题的来源:
http://www.hardkoded.com/blog/csharp-wishlist-extension-for-events
One time generic event call?
答案 0 :(得分:1)
我一直在考虑一种不同但更简单的方法,即使用“自分离”内联处理程序,其使用方式如下:
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
};
答案 1 :(得分:0)
在这种情况下,可以。
ReSharper只是警告handler
在您声明的时间和执行时间之间是不同的。
答案 2 :(得分:0)
不确定您要如何设计扩展方法,但这也许可以帮助您入门:
public static void OnChangedOnce(this FileSystemWatcher instance, Action<object, FileSystemEventArgs> action)
{
var file = instance;
var wrappedAction = action;
FileSystemEventHandler handler = null;
handler = (object sender, FileSystemEventArgs args) =>
{
wrappedAction(sender, args);
file.Changed -= handler;
};
file.Changed += handler;
file.EnableRaisingEvents = true; // mandatory
}
var file = new FileSystemWatcher("path/to/file");
file.OnChangedOnce((sender, args)) =>
{
// your action here
});