通常这样的问题的答案可以在这里找到,如果没有,也可以在MSDN上找到,但我看了又看,没有找到答案。实验似乎表明代码类似:
SomeControl.Click += null;
没有害处。或者,至少触发事件不会通过尝试调用null事件侦听器来抛出异常(我可以判断)。然而,在Web上似乎没有任何地方可以验证我的希望,即这样的代码至少没有做我可能不想做的事情。例如,让Click事件认为它有侦听器,而它可能没有,并在"现在让我们执行null检查并调用所有非null的侦听器"代码中浪费那些宝贵的超迷你微秒。
文档似乎应该说明如果RHS上的侦听器为null,那么+=和-=运算符的行为是什么。但正如我所说,我在任何地方都找不到那份文件。不过,我相信这里有人能提供。。。。
(显然,我的代码没有硬编码的null;我的问题更多的是这样的代码是浪费还是完全无害:
public static void AddHandlers([NotNull] this Button button,
[CanBeNull] EventHandler click = null,
[CanBeNull] EventHandler load = null)
{
button.Click += click;
button.Load += load;
}
或者如果我应该[即,出于任何原因需要]在每个这样的+=操作周围添加空检查。)
考虑以下代码:
void Main()
{
var foo = new Foo();
foo.Blah += Qaz;
foo.Blah += null;
foo.OnBlah();
}
public void Qaz()
{
Console.WriteLine("Qaz");
}
public class Foo
{
public event Action Blah;
public void OnBlah()
{
var b = Blah;
if (b != null)
{
Console.WriteLine("Calling Blah");
b();
Console.WriteLine("Called Blah");
}
}
}
按原样,它运行时没有错误,并产生以下输出:
Calling Blah
Qaz
Called Blah
如果我删除行foo.Blah += Qaz;
,那么代码运行时不会出错,但不会产生任何输出,因此null
处理程序实际上被忽略了。
就IL而言,线路foo.Blah += null;
产生以下IL:
IL_001A: ldloc.0 // foo
IL_001B: ldnull
IL_001C: callvirt Foo.add_Blah
IL_0021: nop
因此,它的行为就像nop
,但它显然运行代码。
答案取决于是将+=
(或-=
)运算符应用于事件还是委托。
在AddHandlers
示例中,Button
类可能将Click
和Load
定义为事件,而不是委托。当其左操作数是一个事件时,+=
运算符只调用该事件的add
访问器,而不进行任何额外的null检查。也就是说,button.Click += value;
只是一个方法调用:
button.add_Click(value);
与任何其他方法一样,add_Click
如何处理null参数完全取决于Button
类。
现在考虑Button
类如何实际实现Click
事件。有一种方法:
private EventHandler _click;
public event EventHandler Click
{
add { _click += value; }
remove { _click -= value; }
}
// Or even shorter...
// public event EventHandler Click;
// ... which is the same as above plus extra stuff for thread safety.
在这个实现中,EventHandler
是一个委托类型。当两个操作数都是委托时,+=
运算符调用Delegate.Combine
。文件显示Delegate.Combine(a, b)
返回:
具有调用列表的新委托,该调用列表按顺序连接A和b的调用列表。如果b为null,则返回a;如果aa为null引用,则返回bb;如果a和b均为null引用则返回null引用。
结论:只要Button
类以"标准"方式实现其事件(而不是在显式List<EventHandler>
中跟踪侦听器),那么button.Click += null;
最终将而不是添加一个null侦听器。