基本上我已经有一段时间了......我想读你的意见
我读过Jon Skeet的伟大着作:C#in Depth,Second Edition,并建议在声明自定义事件时使用类似的东西:
public event Action<string> MyEvent = delegate { };
此声明将在触发事件之前将我们从 nullity check statement 中释放出来,所以不要这样做:
if (this.MyEvent != null)
{
this.MyEvent("OMG osh");
}
我们可以简单地致电:
this.MyEvent("OMG osh");
我们的代码只会起作用。
当您声明这样的事件时,将使用空委托初始化事件,因此我们不需要检查null。
这是宣布事件的另一种方式,它们是等价的
private Action<string> myDelegate;
public event Action<string> MyEvent
{
add
{
this.myDelegate += value;
}
remove
{
this.myDelegate -= value;
}
}
了解更多信息:http://csharpindepth.com/Articles/Chapter2/Events.aspx
我刚刚与一些研究员进行了讨论,在工作中我们正在讨论这个主题,他们争辩说有时我们需要立即清除事件的所有订阅,我们可以简单地将null分配给后面的委托,如果我们想继续使用相同的模式,我们将不得不用空委托重新初始化事件。他们也在询问多线程场景会发生什么,这是我提出这个问题的主要原因
问题
我想知道在声明此模式后的事件与检查null时是否存在一些隐藏的含义(可能在使用多线程时)
如果我使用这种模式,我实际上删除了几个if
条件,这导致删除 jump 语句。在整体表现方面,这不会更好吗?
干杯
答案 0 :(得分:3)
我对性能的兴趣不如开发团队的可读性和期望。你真的不希望其他不了解使用模式的开发人员思考,#啊;啊!我看到有人忘记正确使用委托调用并检查null&#34;
我一直都明白,为了处理多线程场景,你仍然需要制作底层Delegate的本地副本,这样你就可以激活所有订阅者,即使订阅者列表在迭代过程中因订阅者被调用而改变了
说过我仍然想知道使用这个扩展方法来保存样板代码(多个参数的重载)
如果订阅者不需要知道是谁发起了事件,我倾向于选择Action而不是EventHandler事件。
public static class ActionExtension
{
public static void SafeInvoke<T>(this Action<T> action, T arg)
{
var temp = action;
if (temp != null)
{
temp(arg);
}
}
}
public event Action<string> InterestingEvent;
// event invoker
InterestingEvent.SaveInvoke("Boo!");
答案 1 :(得分:1)
首先:你确定你没有过早优化吗?您是否真的面临性能问题,因为在触发事件时,您调用的是另一个空方法而不是检查null?如果没有,那么我建议你放弃这个问题。
现在,对于你的问题:我认为对null 的检查比多余的方法调用便宜是相当安全的,所以如果你想要最好的性能,那么也许是Jon Skeet的方便的delegate { }
模式不适合你。
当谈到多线程时,一个更重要的问题是定义将在哪个线程/哪个上下文(例如SynchronizationContext
)中调用每个事件处理程序。您应该能够将此决定留给您事件的每个消费者,因为某些事件处理程序不关心,其他人希望转发到正确的上下文(例如,通过SynchronizationContext.Post
)。
如果做决定检查null,请注意而不是:
if (this.MyEvent != null)
{
this.MyEvent("OMG osh");
}
建议在多线程场景中,改为:
Action<string> handlers = this.MyEvent;
if (handlers != null)
{
handlers("OMG osh");
}
即,首先将委托复制到局部变量中。这是因为在检查null期间,委托变量可以由另一个线程操纵。 (我不确定这是否真的有必要,但不过这是建议的模式。)
偏离主题:我认为你有一点点错误:
[W]当你宣布一个事件时,实际发生的是你宣布
delegate
和event
。
这是正确的,但值得注意的是,对于CLI,事件只是几种方法的分组(例如add
,remove
,raise
以及可能的其他访问者。几乎所有其他内容实际上都是特定于语言的。例如,C#中的事件只是一个委托,与一些访问器方法配对,以及对委托可以做什么的一些额外限制(即+=
和-=
是唯一有效的操作在声明事件的类型之外的委托上。)