MVVM INotifyPropertyChanged – 线程问题?

我刚开始学习MVVM和WPF,很抱歉提出愚蠢的问题。

我正在使用不同的教程和示例来学习,我遇到了这个例子 (阅读例2),我不明白。

private void RaisePropertyChanged(string propertyName) { // take a copy to prevent thread issues PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } 

基本上, 评论对我来说没有多大意义 ……“拿一份副本来防止线程问题”。

这一行:

 PropertyChangedEventHandler handler = PropertyChanged; 

不创建一个新的,完全不同的handler对象(它没有被克隆)。 它只是对同一PropertyChanged对象的新引用,对吧?

我做了一些测试来找出真正发生的事情:

 PropertyChangedEventHandler handler = PropertyChanged; var message = "PropertyChanged: " + PropertyChanged.GetHashCode() + "\n"; message += "handler: " + handler.GetHashCode() + "\n"; message += "are equal (1): " + (PropertyChanged.Equals(handler)) + "\n"; message += "are equal (2): " + (PropertyChanged == handler) + "\n"; MessageBox.Show(message); 

这是结果:

截图 -  WPF的MVVM,INotifyPropertyChanged的

这证实了我的理论,即这两个对象实际上是相同的 ,并且赋值只是一个NOOP我不明白的是,这与“线程问题”(来自评论)有什么关系?!?

还有一件事:经过一些测试(使用一个非常简单的例子)后,我发现PropertyChanged事件永远不会为null为什么我们需要空检查呢?

在我看来,以前的方法可以简化为:

 private void RaisePropertyChanged(string propertyName) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } 

我测试了一下(再次,在一个非常简单的例子中)它似乎工作得很好 …… 是否有一些我没有找到的捕获或东西? 也许我刚发现了不好的例子?

无论如何,有很多东西我不知道,因为我说我刚刚开始学习WPF和MVVM,但我想了解真正发生的事情 ,不只是采取一些代码而只是粘贴它而不理解为什么以及如何工作。 参见货物崇拜编程和魔术编程 。


编辑

好的,根据答案, 可以在validation和调用之间更改 PropertyChanged事件。 更多, PropertyChanged事件可以为null但是 ,我无法重现这些行为……

有人能给我一个两个陈述发生的例子吗? 它肯定有助于识别类似的情况。

如果你愿意的话

 PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 

您冒着PropertyChanged将为null并且您将获得空引用exception的风险,因此您应该在事件处理程序不为null之前检​​查。 现在,如果你愿意的话

 if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 

在multithreading情况下,您可能会发现PropertyChanged在检查和调用之间将变为null。 为了避免潜在的竞争条件,您将当前委托保留在本地变量中并检查并调用它。

 var handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 

这是安全的,因为

代表是不变的; 创建后,委托的调用列表不会更改

所以即使PropertyChanged要在新委托之间进行更改,但是handler仍然会有你的调用列表,因为它就是你做的那一刻

 var handler = PropertyChanged; 

你的第一个问题已在这里得到解答。

关于你的第二个问题。 如果没有订阅者或所有订阅者都取消订阅,PropertyChanged 可以为null。

当您使用任何绑定到实现INotifyPropertyChanged的对象启动WPF应用程序时,wpf绑定系统会立即挂起到PropertyChanged事件,之后它将不会为null。

比较这两个代码示例:

第一:

 private void RaisePropertyChanged(string propertyName) { // take a copy to prevent thread issues PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } 

第二:

 private void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } 

第二个示例中的问题是PropertyChanged字段可以在if之后立即变为null 。 所以调用PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 将变为null(this, new PropertyChangedEventArgs(propertyName)); 所以它会带你到烦恼之地。 第一个示例没有这样的缺陷,即使任何其他线程会影响PropertyChanged字段,它也会按预期工作。