C# INotifyPropertyChanged 处理程序
C# INotifyPropertyChanged handler
我开始阅读有关 MVVM 的内容,我经常看到的一种模式是:
public event PropertyChangedEventHandler PropertyChanged;
//.....
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
为什么要声明这个 handler
变量?在我看来,它只是不必要地使代码复杂化,但即使在 Microsoft 自己的教程中我也能看到这一点,为什么不直接将其用作:
if (this.PropertyChanged != null)
{
var e = new PropertyChangedEventArgs(propertyName);
this.PropertyChanged(this, e);
}
关闭 PropertyChanged
事件是为了线程安全。事实上,从技术上讲,您应该对 所有 您的事件执行此操作。
赋值创建了事件及其处理程序的副本(不是引用,这没有用),这意味着您可以避免事件处理程序在通过后立即设置为 null
的情况null
检查。这避免了会引发 NullReferenceException 的潜在竞争条件。
实际上,UI 并没有经常将 属性 设置为 null,如果有的话。但是,为了安全起见并采用良好做法,您应该关闭处理程序。
我只想对 BradleyDotNET 的回答补充几点。
Jeffrey Richter 在他的 CLR via C# 一书中指出,该模式仍然不能保证线程安全,因为编译器可以优化掉局部变量(尽管当前版本的编译器没有做到这一点优化)。他建议使用 Volatile.Read
在技术上是正确的:
PropertyChangedEventHandler handler = Volatile.Read(ref PropertyChanged);
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
但实际上,另一种模式的使用如此广泛,以至于 Microsoft 很可能永远不会对编译器做出会破坏如此多应用程序的更改。
还有一点,您实际上可以 add an empty dummy delegate 参加您的活动,如下所示:
public event PropertyChangedEventHandler PropertyChanged = delegate { };
这意味着您不必担心任何空值检查,因为该事件至少附加了一个处理程序作为开始。不过,这种做法是有争议的,因为它似乎会导致性能下降(理论上,您会向每个事件调用添加额外的调用)。我个人避免使用它,因为我宁愿忍受臃肿的代码也不愿忍受臃肿的运行时性能。
This blog post by Eric Lippert 这个话题很好读。
我开始阅读有关 MVVM 的内容,我经常看到的一种模式是:
public event PropertyChangedEventHandler PropertyChanged;
//.....
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
为什么要声明这个 handler
变量?在我看来,它只是不必要地使代码复杂化,但即使在 Microsoft 自己的教程中我也能看到这一点,为什么不直接将其用作:
if (this.PropertyChanged != null)
{
var e = new PropertyChangedEventArgs(propertyName);
this.PropertyChanged(this, e);
}
关闭 PropertyChanged
事件是为了线程安全。事实上,从技术上讲,您应该对 所有 您的事件执行此操作。
赋值创建了事件及其处理程序的副本(不是引用,这没有用),这意味着您可以避免事件处理程序在通过后立即设置为 null
的情况null
检查。这避免了会引发 NullReferenceException 的潜在竞争条件。
实际上,UI 并没有经常将 属性 设置为 null,如果有的话。但是,为了安全起见并采用良好做法,您应该关闭处理程序。
我只想对 BradleyDotNET 的回答补充几点。
Jeffrey Richter 在他的 CLR via C# 一书中指出,该模式仍然不能保证线程安全,因为编译器可以优化掉局部变量(尽管当前版本的编译器没有做到这一点优化)。他建议使用 Volatile.Read
在技术上是正确的:
PropertyChangedEventHandler handler = Volatile.Read(ref PropertyChanged);
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
但实际上,另一种模式的使用如此广泛,以至于 Microsoft 很可能永远不会对编译器做出会破坏如此多应用程序的更改。
还有一点,您实际上可以 add an empty dummy delegate 参加您的活动,如下所示:
public event PropertyChangedEventHandler PropertyChanged = delegate { };
这意味着您不必担心任何空值检查,因为该事件至少附加了一个处理程序作为开始。不过,这种做法是有争议的,因为它似乎会导致性能下降(理论上,您会向每个事件调用添加额外的调用)。我个人避免使用它,因为我宁愿忍受臃肿的代码也不愿忍受臃肿的运行时性能。
This blog post by Eric Lippert 这个话题很好读。