简体   繁体   English

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

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

What is the difference between these two code samples for invoking an event? 这两个代码示例之间用于调用事件有什么区别?

Sample 1 样品1

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

Sample 2 样本2

DataChanged.Invoke(this);

When should I use each method to invoke a custom event? 我应该何时使用每种方法来调用自定义事件? And why sometimes I get a NullReferenceException when I try to invoke event using DataChanged.Invoke(this) , but when I convert the event invocation to the method in Sample 1 the DataChanged never becomes null anymore? 为什么有时我尝试使用DataChanged.Invoke(this)调用事件时会得到NullReferenceException,但是当我将事件调用转换为示例1中的方法时, DataChanged永远不会变为null?

An OnXYZ method should always follow this form: OnXYZ方法应始终遵循以下形式:

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

There are a couple of reasons for this form: 这种形式有几个原因:

  1. The if evt != null check ensures that we don't try to invoke a null delegate. if evt != null检查确保我们不会尝试调用null委托。 This can happen if nobody has hooked up an event handler to the event. 如果没有人将事件处理程序连接到事件,就会发生这种情况。
  2. In a multithreaded scenario, since delegates are immutable, once we've obtained a local copy of the delegate into evt , we can safely invoke it after checking for non-null, since nobody can alter it after the if but before the call. 在多线程场景中,由于委托是不可变的,一旦我们获得委托的本地副本到evt ,我们可以在检查非null之后安全地调用它,因为没有人可以在if但在调用之前改变它。

What to pass for e differs, if you need to pass an EventArgs descendant with a parameter there are two ways: e传递的内容有所不同,如果需要使用参数传递EventArgs后代,有两种方法:

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

or more commonly this: 或者更常见的是:

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

This syntax: 这个语法:

evt(sender, e);

is just a different way of writing this: 只是一种不同的写作方式:

evt.Invoke(sender, e);

Also note that externally to your class, the event is an event, you can only add or remove event handlers from it. 另请注意,在您的类外部,事件是一个事件,您只能从中addremove事件处理程序。

Internal to your class, the event is a delegate, you can invoke it, check the target or method, walk the list of subscribers, etc. 在您的类的内部,事件是委托,您可以调用它,检查目标或方法,遍历订阅者列表等。


Also, in C# 6 there is a new operator introduced, ?. 此外,在C#6中引入了一个新的运算符, ?. - the Null-conditional operator - which is basically short for if not-null, dereference , which can shorten this method: - Null条件运算符 - 它基本上是if not-null, dereference缩写,可以缩短这个方法:

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

into this: 进入这个:

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

which can be further shortened with the use of Expression-bodied members: 可以通过使用表达身体成员进一步缩短:

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

Please note that it is not possible to write this: 请注意,不能写这个:

XYZ?.(sender, e);

so you must in this case use Invoke yourself. 所以你必须在这种情况下使用Invoke

when I convert the event invocation to the method in Sample 1 the DataChanged never becomes Null 当我将事件调用转换为示例1中的方法时,DataChanged永远不会变为空

Then you're simply looking at two different scenarios. 然后你只是看两种不同的场景。

if you don't declare an event like public event EventHandler YourEvent = delegate { }; 如果你没有声明像public event EventHandler YourEvent = delegate { };那样的public event EventHandler YourEvent = delegate { }; , then YourEvent is null until some consumer subscribes to it. ,然后YourEventnull直到某些消费者订阅它。

If nothing has subscribed to DataChanged it will be set to null, and so when you attempt to do DataChanged.Invoke(this) you'll get a NullRefException as really it's trying to do null.Invoke(this). 如果没有订阅DataChanged它将被设置为null,所以当你尝试执行DataChanged.Invoke(this)时,你会得到一个NullRefException,因为它真的试图做null.Invoke(this)。 The reason for the additional if (DataChanged != null) is to avoid this occurring when nobody has subscribed to the event. 附加if(DataChanged!= null)的原因是为了避免在没有人订阅该事件时发生这种情况。

I don't believe that when you use Sample 1 DataChanged is never null, it's just never reaching the .Invoke to throw up the exception. 我不相信当你使用Sample 1 DataChanged永远不会为null时,它永远不会到达.Invoke来抛出异常。 It will always be null if nobody has subscribed. 如果没有人订阅,它将始终为null。

Are you sure, that in Sample 1 the DataChanged is never null? 您确定,在示例1中, DataChanged永远不会为空吗? Or you just don't get the NullReference Exception (because you check if DataChanged is not null in the if statement)? 或者你只是没有得到NullReference异常(因为你检查if语句中的DataChanged 是否为空 )?

Let's start with the basics. 让我们从基础开始。 Event is a special kind of delegate. 活动是一种特殊的代表。 When you call DataChanged(this) and DataChanged.Invoke(this), it's the same thing. 当你调用DataChanged(this)和DataChanged.Invoke(this)时,它是一样的。 Why? 为什么? Because it compiles to the same thing. 因为它编译成同样的东西。 So all in all the DataChanged(this) is just a shorthand for calling DataChanged.Invoke(this) . 总而言之, DataChanged(this)只是调用DataChanged.Invoke(this)的简写。

Now, why do we need to check for null reference (like in sample 1). 现在,为什么我们需要检查空引用(如示例1中所示)。 Basically, when you invoke an event, you invoke all methods that were subscribed to this event (eg DataChanged += someEventHandler ). 基本上,当您调用事件时,您将调用订阅此事件的所有方法(例如, DataChanged += someEventHandler )。 If nobody subscribed to this event, it will have a null value. 如果没有人订阅此事件,它将具有null值。 No method was assigned to handle this event. 没有分配方法来处理此事件。 In other words: the event handler is null. 换句话说:事件处理程序为null。

That's why it's a good practice to check for null, before invoking an event. 这就是为什么在调用事件之前检查null是一个好习惯。

An Example: 一个例子:

public void OnAbc(){
    var data=Abc;

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

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

相关问题 我应该在Invoke()之前检查InvokeRequired吗? - Should I check InvokeRequired before Invoke()? NUnit:在断言中使用对象之前,我是否应该检查对象是否不是 null? - NUnit: Should I check that objects are not null before using them in asserts? 为什么要在调用 ThrowIfCancellationRequested() 之前检查 IsCancellationRequested - Why should I check IsCancellationRequested before calling ThrowIfCancellationRequested() 为什么我可以检查一些事件处理程序为null,有些不是吗? - Why can I check some event handlers for null, some not? 我应该在删除之前检查用户是否存在吗? - Should I check if a user exists before deleting it? 为什么在调用事件之前需要附加的if语句来检查事件是否为null? - Why is an additional if statement to check if an event is null before invoking it? 在linq中使用之前如何检查null? - How to check for null before I use in linq? 我应该在查询中检查null还是dbnull - Should I check for null or dbnull in the query 我应该检查WebOperationContext.Current是否为null? - Should I check WebOperationContext.Current is null? 为什么我应该在 c# 中的 System.Action 之前使用消息/信号/事件库? - Why should I use a Messaging/Signal/Event library before simply System.Action in c#?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM