C# 初始化事件与检查null

C# 初始化事件与检查null,c#,C#,基本上这在我脑子里已经有一段时间了。。。我想听听你的意见 我读了Jon Skeet的一本很棒的书,名为:C#深入,第二版,在声明自定义事件时,有一个建议使用如下内容: public event Action<string> MyEvent = delegate { }; 我们可以简单地称之为: this.MyEvent("OMG osh"); 我们的代码将简单地工作 当您声明这样的事件时,事件将使用空委托进行初始化,因此我们不需要检查null 这是声明事件的另一种方式,它们是等价

基本上这在我脑子里已经有一段时间了。。。我想听听你的意见

我读了Jon Skeet的一本很棒的书,名为:C#深入,第二版,在声明自定义事件时,有一个建议使用如下内容:

public event Action<string> MyEvent = delegate { };
我们可以简单地称之为:

this.MyEvent("OMG osh");
我们的代码将简单地工作

当您声明这样的事件时,事件将使用空委托进行初始化,因此我们不需要检查null

这是声明事件的另一种方式,它们是等价的

private Action<string> myDelegate;
public event Action<string> MyEvent
{
    add
    {
        this.myDelegate += value;
    }
    remove
    {
        this.myDelegate -= value;
    }
}
私人行动myDelegate;
公共事件行动
{
添加
{
this.myDelegate+=值;
}
去除
{
this.myDelegate-=值;
}
}
有关更多信息:

我刚刚和一些同事在工作中讨论了这个话题,他们认为有时我们需要立即清除所有活动订阅,我们可以简单地将null赋值给后面的委托,如果我们想继续使用相同的模式,就必须用一个空委托重新初始化事件。他们还问在多线程场景中会发生什么,这就是我提出这个问题的主要原因

问题

  • 我想知道当按照这种模式声明事件与检查null时,是否存在一些隐藏的含义(可能是在使用多线程时)

  • 如果我使用这种模式,我将有效地删除几个
    If
    条件,从而删除跳转语句。就整体性能而言,这不是更好吗


  • 干杯首先:你确定你没有过早地优化吗?您是否确实面临性能问题,因为在触发事件时,您不是在检查null,而是在调用另一个空方法?如果没有,那么我建议你放弃这个问题

    现在,对于您的问题:我认为可以肯定地说,检查null比多余的方法调用更便宜,因此如果您希望获得尽可能好的性能,那么Jon Skeet方便的
    委托{}
    模式可能不适合您

    当涉及多线程时,一个更重要的问题是定义每个事件处理程序将在哪个线程/哪个上下文(例如,
    SynchronizationContext
    )上调用。您应该能够将此决定权留给事件的每个使用者,因为有些事件处理程序不关心,而另一些则希望转发到正确的上下文(例如通过
    SynchronizationContext.Post

    如果您决定检查null,请注意以下内容:

        if (this.MyEvent != null)
        {
            this.MyEvent("OMG osh");
        }
    
    if (this.MyEvent != null)
    {
        this.MyEvent("OMG osh");
    }
    
    建议在多线程方案中,改为执行以下操作:

    Action<string> handlers = this.MyEvent;
    if (handlers != null)
    {
        handlers("OMG osh");
    }
    
    Action handlers=this.MyEvent;
    if(处理程序!=null)
    {
    装卸工(“OMG osh”);
    }
    
    也就是说,首先将委托复制到局部变量中。这是因为在检查null期间,委托变量可能会被另一个线程操纵。(我不确定这是否真的有必要,但这是建议的模式。)


    离题:我想你有一个小错:

    [W] 当您声明一个事件时,[正在]实际发生的是您正在声明一个
    委托
    和一个
    事件


    这是正确的,但值得注意的是,对于CLI来说,事件只是几个方法的组合(例如,
    add
    remove
    raise
    以及其他可能的访问器)。几乎所有其他东西实际上都是特定于语言的。例如,C#中的事件只不过是一个委托,与一些访问器方法配对,以及对委托可以执行的操作的一些附加约束(即
    +=
    -=
    是从声明事件的类型之外对委托执行的唯一有效操作).

    比起开发团队的可读性和期望,我对性能更感兴趣。您不希望其他不了解使用模式的开发人员认为,“啊!我看到有人忘记正确使用委托调用并检查null”

    我一直都明白,要处理多线程场景,您仍然需要制作底层委托的本地副本,这样您就可以向所有订阅者发出请求,即使在调用订阅者时,在迭代过程中订阅者列表发生了更改

    尽管如此,我仍然想知道如何使用这个扩展方法来保存样板代码(对于多个参数使用重载)

    如果订阅者不需要知道是谁触发了事件,我倾向于选择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!");
    
    公共静态类ActionExtension
    {
    公共静态void SafeInvoke(此操作,T参数)
    {
    var-temp=动作;
    如果(温度!=null)
    {
    温度(arg);
    }
    }
    }
    公共事件行动兴趣事件;
    //事件调用程序
    SaveInvoke(“Boo!”);
    
    Erm。。。对不起,但是。。。你的问题是什么?这都是非常有趣的聊天内容,但问答网站也是如此。你能把引用的文本格式化得更清楚些吗?(选择并使用
    [“]
    按钮)这篇文章的结尾,我只是强调我想阅读你对这个主题的评论->不是正确的问题。@Jupaol我建议将你的问题移到文本的开头,然后在下面添加你的其他评论。