繁体   English   中英

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

[英]Why should I check for null before I invoke the custom event?

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

样品1

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

样本2

DataChanged.Invoke(this);

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

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但在调用之前改变它。

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条件运算符 - 它基本上是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中的方法时,DataChanged永远不会变为空

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

如果你没有声明像public event EventHandler YourEvent = delegate { };那样的public event EventHandler YourEvent = delegate { }; ,然后YourEventnull直到某些消费者订阅它。

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

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

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

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

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

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

一个例子:

public void OnAbc(){
    var data=Abc;

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM