我刚开始学习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);
结果如下:
这证实了我的理论,即这2个对象实际上是相同的,而赋值只是 NOOP 。 我不明白的是,这与线程问题"有什么关系? (来自评论)?!?
还有一件事:经过一些测试(使用一个非常简单的例子)后,我发现PropertyChanged
事件永远不会为空。 为什么我们需要进行空检查?
在我看来,以前的方法可以简化为:
private void RaisePropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
我测试了一下(再次,在一个非常简单的例子中)并且似乎工作得非常好 ... 是否有一些我找不到的东西或东西?也许我刚发现了不好的例子?
无论如何,有很多我不知道的东西因为我说我刚开始学习WPF和MVVM,但是我想了解真正发生的事情,而不是只需要一些代码并粘贴它而不理解为什么以及如何工作。请参阅cargo cult programming和magic programming。
好的,根据答案,可以在验证和通话之间更改<{1}}事件。此外,PropertyChanged
事件可以为null 。 然而,我还没能重现这些行为......
有人能给我一个两个陈述都发生的例子吗?这肯定有助于识别类似的情况。
答案 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
字段,它也会按预期工作。