MVVM INotifyPropertyChanged - 线程问题?

时间:2014-03-08 11:02:54

标签: c# wpf mvvm

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

我使用不同的教程和示例来学习,我遇到this example(阅读例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);

结果如下:

screenshot-wpf-mvvm-inotifypropertychanged

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

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

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

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

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

无论如何,有很多我不知道的东西因为我说我刚开始学习WPF和MVVM,但是我想了解真正发生的事情,而不是只需要一些代码并粘贴它而不理解为什么以及如何工作。请参阅cargo cult programmingmagic programming


修改

好的,根据答案,可以在验证和通话之间更改<{1}}事件。此外,PropertyChanged事件可以为null 然而,我还没能重现这些行为......

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

3 个答案:

答案 0 :(得分:6)

如果你只做

PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

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

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

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

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

这是安全的,因为

  

Delegates是不可改变的;一旦创建,代理的调用列表不会改变

所以,即使PropertyChanged之间要更改,也会创建新的委托,但handler仍会保留您的调用列表,就像您当时所做的那样

var handler = PropertyChanged;

答案 1 :(得分:1)

您的第一个问题已经回答here

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

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

答案 2 :(得分:0)

比较这两个代码示例:

首先:

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字段,它也会按预期工作。