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的必要性.
但是,我很想知道是否有任何缺点. 例如,它是否被广泛使用并且足够透明以至于不会引起维护问题?空事件用户呼叫是否有明显的性能影响?
为了引发事件,我们使用OnEventName方法,如下所示:
protected virtual void OnSomethingHappened(EventArgs e)
{
EventHandler handler = SomethingHappened;
if (handler != null)
{
handler(this, e);
}
}
Run Code Online (Sandbox Code Playgroud)
但这个有什么不同?
protected virtual void OnSomethingHappened(EventArgs e)
{
if (SomethingHappened!= null)
{
SomethingHappened(this, e);
}
}
Run Code Online (Sandbox Code Playgroud)
显然第一个是线程安全的,但为什么以及如何?
没有必要开始一个新线程?
我在微软实践测试中遇到了这个问题,我感到很困惑.这是一个问题:
假设已声明了Alarm事件,AlarmEventArgs类和AlarmEventHandler委托,以下哪个C#代码示例是引发事件的正确方法?
以下是他们提供的"正确"答案:
AlarmEventArgs e = new AlarmEventArgs(1, 2);
AlarmEventHandler handler = Alarm;
if (handler != null)
{
handler(this, e);
}
Run Code Online (Sandbox Code Playgroud)
但是,还有另一个看似正确的答案.
AlarmEventArgs e = new AlarmEventArgs(1, 2);
if (Alarm!= null)
{
Alarm (this, e);
}
Run Code Online (Sandbox Code Playgroud)
我个人总是使用第二种方法.它工作得很好.有人可以告诉我为什么我应该使用第一种方法而不是第二种方法?
大多数情况下,当我们使用MVVM时,我们使用INotifyPropertyChanged接口向绑定提供通知,一般实现如下所示:
public class MyClass : INotifyPropertyChanged
{
// properties implementation with RaisePropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Run Code Online (Sandbox Code Playgroud)
每当我从专家那里读到代码时,这对我来说都很好 - 他们写了类似的代码:
public class MyClass : INotifyPropertyChanged
{
// properties implementation with RaisePropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var tempchanged = PropertyChanged;
if (tempchanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Run Code Online (Sandbox Code Playgroud)
我想知道为PropertyChanged事件创建临时对象的确切原因是什么.
这只是一种好的做法,还是有任何与之相关的其他好处?
我找到了Jon的回答和解释的例子:
以下是了解此问题的示例代码:
using System;
using System.Collections.Generic;
using …Run Code Online (Sandbox Code Playgroud) C#6.0添加了这个新的?.运算符,现在允许调用这样的事件:
someEvent?.Invoke(sender, args);
Run Code Online (Sandbox Code Playgroud)
现在,根据我的阅读,这个运算符保证someEvent被评估一次.使用这种调用而不是经典模式是否正确:
var copy = someEvent
if(copy != null)
copy(sender, args)
Run Code Online (Sandbox Code Playgroud)
我知道某些情况下上面版本的模式需要额外的锁,但让我们假设最简单的情况.
例如,实现INotifyPropertyChanged接口:
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Run Code Online (Sandbox Code Playgroud)
两件事情:
NullReferenceException但是现在,我们可以使用?.运算符进行空值检查.如果我使用它,Resharper是空闲的:

所以,问题是:ProperyChanged如果我使用空条件运算符,我应该将事件复制到局部变量吗?
这就是我一直写的事件提升者; 例如PropertyChanged:
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string name)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(name));
}
Run Code Online (Sandbox Code Playgroud)
然而,在最新的Visual Studio中,灯泡thingamabob建议将代码简化为:
private void RaisePropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
Run Code Online (Sandbox Code Playgroud)
虽然我总是赞成简化,但我想确保这是安全的.在我的原始代码中,我将处理程序分配给变量以防止订阅者在空检查和调用之间处置的竞争条件.在我看来,新的简化形式将遭受这种情况,但我想看看是否有人可以确认或否认这一点.
原子操作如何成为线程安全的?我在维基百科关于线程安全的文章中读过这个主题.但这篇文章并没有真正解释幕后的过程.换句话说,为什么线程A执行的"原子"操作不能被线程B中断?
对事件对象执行空检查时,Visual Studio会将代码颜色更改为浅灰色。如果我没记错的话,通常意味着这行是不必要的。显然,在我的情况下不是这样(或者是?)。请参阅下面的示例:
public event PropertyChangedEventHandler PropertyChanged;
string test;
protected void OnPropertyChanged (string propertyName)
{
if (PropertyChanged != null) { //This line is grey
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
} //This line is grey
if (test != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Run Code Online (Sandbox Code Playgroud)