7

大多数情况下,当我们使用 MVVM 时,我们使用 INotifyPropertyChanged 接口向绑定提供通知,一般实现如下所示:

public class MyClass : INotifyPropertyChanged
{
    // properties implementation with RaisePropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

每当我阅读专家的代码时,这对我来说都很好 - 他们编写了类似的代码:

public class MyClass : INotifyPropertyChanged
{
    // properties implementation with RaisePropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged(string propertyName)
    {
        var tempchanged = PropertyChanged;
        if (tempchanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

我想知道为 PropertyChanged 事件创建临时对象的确切原因是什么。

这只是一个好的做法还是有任何其他相关的好处?

我在乔恩的回答和解释的例子中找到了答案:

理解 C#:使用临时变量引发事件

这是理解这一点的示例代码:

using System;
using System.Collections.Generic;
using System.Threading;

class Plane
{
     public event EventHandler Land;

     protected void OnLand()
     {
          if (null != Land)
          {
               Land(this, null);
           }
      }

     public void LandThePlane()
     {
          OnLand();
      }
}

class Program
{
     static void Main(string[] args)
     {
          Plane p = new Plane();
          ParameterizedThreadStart start = new ParameterizedThreadStart(Run);
          Thread thread = new Thread(start);
          thread.Start(p);

          while (true)
          {
               p.LandThePlane();
           }
      }

     static void Run(object o)
     {
          Plane p = o as Plane;
          while (p != null)
          {
               p.Land += p_Land;
               p.Land -= p_Land;
           }
      }

     static void p_Land(object sender, EventArgs e)
     {
          return;
      }
}
4

4 回答 4

25

您不是在创建临时对象。您正在使用局部变量来避免竞争条件。

在这段代码中:

if (PropertyChanged != null)
{
    PropertyChanged(...);
}

在无效性检查之后有可能PropertyChanged成为null(由于最后一个订阅者取消订阅),这意味着你得到一个.NullReferenceException

当您使用局部变量时,请确保您检查是否为 null 的引用与您用来引发事件的引用相同 - 因此您不会得到异常。仍然存在竞争条件,您可能最终会呼叫刚刚退订的订户,但这是不可避免的。

于 2013-07-02T13:21:33.260 回答
4

这是为了避免在您检查 null(以查看是否附加了任何事件处理程序)和调用事件之间的最后一个(或唯一一个)事件处理程序从事件中删除的罕见情况。如果发生这种情况,您将获得一个NullReferenceException.

如果您担心内存泄漏 - 不要担心 - 它只是一个引用,而不是事件处理程序的副本。

更多细节可以在这里找到

于 2013-07-02T13:22:25.910 回答
1

出于线程安全的原因,这是一种很好的做法。

在您的原始代码中,理论上可以让单独的线程在语句之后但在下一行引发事件之前PropertyChanged删除处理程序。这会导致.ifNullReferenceException

第二个样本消除了这种风险。

于 2013-07-02T13:22:48.390 回答
0

只有在处理多线程场景时才会有所不同:当最后一个事件处理程序在!= null检查后注销时,实际调用可能会遇到代码 1 中的 NullReferenceException。

然而,代码 2 没有这个问题,因为委托(事件背后的概念)是不可变的,因此临时变量的值不能改变。

但是,我建议始终使用变体 2 作为最佳实践 - 这可能会让您在未来感到头疼;-)

于 2013-07-02T13:22:30.183 回答