在调用自定义事件之前,为什么要检查null?

时间:2015-10-14 10:03:32

标签: c# events

这两个用于调用事件的代码示例有什么区别?

样本1

public void OnDataChanged()
{
    if (DataChanged != null)
    {
        DataChanged(this);
    }
}

样本2

DataChanged.Invoke(this);

我应该何时使用每种方法来调用自定义事件?为什么有时候我尝试使用DataChanged.Invoke(this)调用事件时会得到NullReferenceException,但是当我将事件调用转换为示例1中的方法时,DataChanged永远不会变为null?

5 个答案:

答案 0 :(得分:15)

OnXYZ方法应始终遵循以下格式:

public void OnXYZ()
{
    var evt = XYZ;
    if (evt != null)
        evt(sender, e); // where to get e from differs
}

这种形式有几个原因:

  1. if evt != null检查可确保我们不会尝试调用null代理人。如果没有人将事件处理程序连接到事件,就会发生这种情况。
  2. 在多线程场景中,由于委托是不可变的,一旦我们获得委托的本地副本到evt,我们可以在检查非null后安全地调用它,因为没有人可以改变它在if之后但在通话之前。
  3. e传递的内容有所不同,如果您需要使用参数传递EventArgs后代,则有两种方法:

    public void OnXYZ(string p)
    {
        var evt = XYZ;
        if (evt != null)
            evt(sender, new SomeEventArgs(p));
    }
    

    或更常见的是:

    public void OnXYZ(SomeEventArgs e)
    {
        var evt = XYZ;
        if (evt != null)
            evt(sender, e);
    }
    

    此语法:

    evt(sender, e);
    

    只是另一种写作方式:

    evt.Invoke(sender, e);
    

    另请注意,在您的课程外部,该活动是一个活动,您只能addremove个活动处理程序。

    您的类的内部,事件是委托,您可以调用它,检查目标或方法,遍历订阅者列表等。

    此外,在C#6中引入了一个新的运算符?. - Null-conditional operator - 这基本上是if not-null, dereference的缩写,可以缩短此方法:

    public void OnXYZ(SomeEventArgs e)
    {
        var evt = XYZ;
        if (evt != null)
            evt(sender, e);
    }
    

    进入这个:

    public void OnXYZ(SomeEventArgs e)
    {
        XYZ?.Invoke(sender, e);
    }
    

    可以通过使用表达身体的成员进一步缩短:

    public void OnXYZ(SomeEventArgs e) => XYZ?.Invoke(sender, e);
    

    请注意,无法写下:

    XYZ?.(sender, e);
    

    所以在这种情况下你必须自己使用Invoke

答案 1 :(得分:2)

  

当我将事件调用转换为示例1中的方法时,DataChanged永远不会变为空

然后你只是看两种不同的场景。

如果您未声明 System.out.println(TimeZone.getTimeZone("Australia/Sydney").inDaylightTime(new Date())); System.out.println(TimeZone.getTimeZone("Europe/Moscow").inDaylightTime(new Date())); 之类的事件,则public event EventHandler YourEvent = delegate { };YourEvent,直到某个消费者订阅它为止。

答案 2 :(得分:1)

如果没有订阅DataChanged它将被设置为null,所以当你尝试执行DataChanged.Invoke(this)时,你会得到一个NullRefException,因为它真的试图做null.Invoke (这个)。附加if(DataChanged!= null)的原因是为了避免在没有人订阅该事件时发生这种情况。

我不相信当你使用Sample 1 DataChanged永远不会为null时,它永远不会到达.Invoke来抛出异常。如果没人订阅,它将始终为null。

答案 3 :(得分:1)

您确定,在示例1中,DataChanged永远不会为空吗?或者你只是没有得到NullReference异常(因为你在DataChanged语句中检查if 是否为空)?

让我们从基础开始。活动是一种特殊的代表。当你调用DataChanged(this)和DataChanged.Invoke(this)时,它是一回事。为什么?因为它编译成同样的东西。总而言之,DataChanged(this)只是调用DataChanged.Invoke(this)的简写。

现在,为什么我们需要检查空引用(如示例1中所示)。 基本上,当您调用事件时,您将调用订阅此事件的所有方法(例如DataChanged += someEventHandler)。 如果没有人订阅此活动,则其值为null。没有分配方法来处理此事件。换句话说:事件处理程序为空。

这就是为什么在调用事件之前检查null是一个好习惯。

答案 4 :(得分:0)

一个例子:

public void OnAbc(){
    var data=Abc;

    if(!String.IsNullOrEmpty(data))
        Abc(sender,e);
}