UPDATE
从C#6开始,这个问题的答案是:
SomeEvent?.Invoke(this, e);
Run Code Online (Sandbox Code Playgroud)
我经常听到/阅读以下建议:
在检查事件之前,请务必复制事件null并将其触发.这将消除线程的潜在问题,其中事件变为null位于您检查null和触发事件的位置之间的位置:
// Copy the event delegate before checking/calling
EventHandler copy = TheEvent;
if (copy != null)
copy(this, EventArgs.Empty); // Call any handlers on the copied list
Run Code Online (Sandbox Code Playgroud)
更新:我从阅读中了解到这可能还需要事件成员的优化,但Jon Skeet在他的回答中指出CLR不会优化副本.
但同时,为了解决这个问题,另一个线程必须做到这样的事情:
// Better delist from event - don't want our handler called from now on:
otherObject.TheEvent -= OnTheEvent;
// Good, now we can be certain that OnTheEvent will not run...
Run Code Online (Sandbox Code Playgroud)
实际的顺序可能是这种混合物:
// Copy the event delegate before checking/calling
EventHandler copy …Run Code Online (Sandbox Code Playgroud) 我已经看到了一些关于这个习语的提及(包括SO):
// Deliberately empty subscriber
public event EventHandler AskQuestion = delegate {};
Run Code Online (Sandbox Code Playgroud)
好处很明显 - 它避免了在提升事件之前检查null的必要性.
但是,我很想知道是否有任何缺点. 例如,它是否被广泛使用并且足够透明以至于不会引起维护问题?空事件用户呼叫是否有明显的性能影响?
目前" 避免检查空事件处理程序"是标题为C#隐藏功能的帖子的答案的顶部,它包含严重误导性信息.
虽然我理解Stack Overflow是一个"民主",并且答案因公众投票而上升到顶峰,我觉得很多投票给答案的人要么没有完全理解C#/ .NET或者没有花时间充分了解帖子中描述的做法的后果.
简而言之,该帖子主张使用以下构造来避免在调用事件时检查null.
public event EventHandler SomeEvent = delegate {};
// Later..
void DoSomething()
{
// Invoke SomeEvent without having to check for null reference
SomeEvent(this, EventArgs.Empty);
}
Run Code Online (Sandbox Code Playgroud)
乍一看,这似乎是一个聪明的捷径,但它可能是大型应用程序中一些严重问题的原因,特别是如果涉及并发性.
在调用事件的委托之前,您必须检查空引用.仅仅因为您使用空委托初始化事件并不意味着您的类的用户不会在某些时候将其设置为null并破坏您的代码.
像这样的东西是典型的:
void DoSomething()
{
if(SomeEvent != null)
SomeEvent(this, EventArgs.Empty);
}
Run Code Online (Sandbox Code Playgroud)
但即使在上面的例子中,也存在这样的可能性,即DoSomething()可能由一个线程运行,另一个可能会删除事件处理程序,并且可能会出现竞争条件.
假设这种情况:
Thread A. Thread B.
-------------------------------------------------------------------------
0: if(SomeEvent != null)
1: { // remove all handlers of SomeEvent
2: SomeEvent(this, EventArgs.Empty);
3: }
在引发事件的代码检查了委托以获取空引用之后,但在调用委托之前,线程B删除了SomeEvent事件的事件处理程序.当SomeEvent(this,EventArgs.Empty); 调用,SomeEvent为null并引发异常.
为了避免这种情况,提出事件的更好模式是:
void DoSomething()
{
EventHandler handler …Run Code Online (Sandbox Code Playgroud) 我对MVVM比较陌生,我试图了解INotifyPropertyChanged接口的工作原理以及如何在我的模型中实现它.我决定采用的方法是在每个Business Object类中实现它.
这种方法的问题在于,当我将View绑定到Base类中的属性时,该基类中的PropertyChanged事件永远不会被初始化(为null),因此当我的Model更改时,View不会刷新该元素的数据.我能够通过下面的例子重现问题.
我有一个Person Base类:
public class Person : INotifyPropertyChanged
{
#region INotifyProperty
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
public String Name
{
get
{
return _name;
}
set
{
_name = value;
RaisePropertyChanged("Name");
}
}
private String _name;
}
Run Code Online (Sandbox Code Playgroud)
我有一个继承自Person Base类的Employee类:
public class Employee : Person,INotifyPropertyChanged
{
#region INotifyProperty
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new …Run Code Online (Sandbox Code Playgroud)