PropertyChangedEventHandler是如何工作的?

20

这是一个非常简单的问题,但我想知道第四行实际上在做什么?第一行将事件交给处理程序。我不知道处理程序在什么情况下会返回null或者最后一行代码是干什么用的。

当您将对象和更改的属性传递给处理程序时,它会执行什么操作?

PropertyChangedEventHandler handler = PropertyChanged; //property changed is the event

if (handler != null)
{
    handler(this, new PropertyChangedEventArgs(name));
}

我假设我使用这个代码来得到这个结果,但我想完全理解它在做什么。

我假设我使用这个代码来得到这个结果,但我想完全理解它在做什么。


没有上下文很难判断。你正在使用什么框架?那不是BCL的一部分,这可能是你正在检查/使用的某个MVVM框架。 - user1228
这对于 PropertyChangedEventHandler 来说并没有什么特别之处。这就是处理程序的工作方式。 - John Saunders
Will的评论毫无意义。这个标签是WPF。PropertyChangedEventHandler在System.ComponentModel中。没有所谓的MVVM框架- MVVM是一种与WPF有用但不必要的架构模式。无论如何,这个问题涉及到C#事件处理程序的工作方式,答案并不特定于任何特定的事件或框架。 - Jim Balter
第一行是将事件绑定到处理程序吗?不,它是声明一个变量 handler,并将其初始化为 PropertyChanged 的值...这个值可能为 null。 - Jim Balter
3个回答

43
如果你刚刚执行了:
PropertyChanged(this, new PropertyChangedEventArgs(name))

如果没有人订阅事件PropertyChanged,则会引发NullReferenceException异常。为了解决这个问题,您可以添加一个空值检查:

if(PropertyChanged != null)
{
    PropertyChanged(this, new PropertyChangedEventArgs(name))
}

现在,如果您正在使用多线程,可能会在空值检查和调用事件之间取消订阅,因此仍然可能会收到 NullReferenceException。为了处理这种情况,我们将事件处理程序复制到临时变量中。

  PropertyChangedEventHandler handler = PropertyChanged;
  if (handler != null)
  {
    handler(this, new PropertyChangedEventArgs(name));
  }
现在,如果有人取消订阅事件,我们的临时变量handler仍将指向旧函数,而此代码现在没有办法抛出NullReferenceException
通常你会看到人们使用关键字var,这样你就不需要键入临时变量的完整类型,这是你最经常在代码中看到的形式。
  var handler = PropertyChanged;
  if (handler != null)
  {
    handler(this, new PropertyChangedEventArgs(name));
  }

在竞态条件下,复制处理程序然后调用某人刚指定不再调用的函数,这样做是否可行?这只是业务惯例还是我漏掉了什么? - Zach Mierzejewski
1
@ZachMierzejewski 是的,这是预期的行为。订阅者有责任知道,即使取消订阅,如果处于多线程情况下,仍然可能会最终调用该方法。.NET内置类的所有事件都遵循此模式。 - Scott Chamberlain

10

PropertyChanged是一个事件,根据其在接口中的定义如下所示:

public event PropertyChangedEventHandler PropertyChanged;

如此定义的事件实际上是一系列事件处理程序的语法糖,您可以通过订阅将委托(即对函数的引用)添加到其中,也可以通过取消订阅来删除委托。

现在,当您调用事件,例如PropertyChanged(...)时,内部会分别调用该内部列表中的每个委托,使用参数告知事件的所有订阅者事件已发生。

现在,使用handler变量的原因是PropertyChanged可能为空。如果没有任何订阅者,那么调用事件(或者说事件处理程序列表)将不起作用,所以这只是确保您可以实际执行处理程序的方法。


6

handler如果没有任何处理程序订阅事件,则可以为null。 第四行引发给定属性名称的事件(这将执行所有已订阅的处理程序)。

通常情况下,当使用绑定时,WPF框架会订阅PropertyChanged,以便在绑定属性更改时更新绑定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接