Initializeng事件与检查null

时间:2012-07-18 07:40:24

标签: c#

基本上我已经有一段时间了......我想读你的意见

我读过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分配给后面的委托,如果我们想继续使用相同的模式,我们将不得不用空委托重新初始化事件。他们也在询问多线程场景会发生什么,这是我提出这个问题的主要原因

问题

  1. 我想知道在声明此模式后的事件与检查null时是否存在一些隐藏的含义(可能在使用多线程时)

  2. 如果我使用这种模式,我实际上删除了几个if条件,这导致删除 jump 语句。在整体表现方面,这不会更好吗?

  3. 干杯

2 个答案:

答案 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]当你宣布一个事件时,实际发生的是你宣布delegateevent

这是正确的,但值得注意的是,对于CLI,事件只是几种方法的分组(例如addremoveraise以及可能的其他访问者。几乎所有其他内容实际上都是特定于语言的。例如,C#中的事件只是一个委托,与一些访问器方法配对,以及对委托可以做什么的一些额外限制(即+=-=是唯一有效的操作在声明事件的类型之外的委托上。)