为什么我们应该使用临时对象来引发事件?

时间:2013-07-02 13:18:40

标签: c# .net wpf

大多数情况下,当我们使用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事件创建临时对象的确切原因是什么。

这只是一种好的做法,还是有任何与之相关的其他好处?

我找到了Jon的回答和解释的例子:

Understanding C#: Raising events using a temporary variable

以下是了解此问题的示例代码:

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 个答案:

答案 0 :(得分:25)

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

在此代码中:

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

PropertyChanged {<1}}可能会成为null(由于最后一个订阅者取消订阅)之后进行无效检查,这意味着您将获得NullReferenceException

当您使用局部变量时,您确保检查无效的引用与用于引发事件的引用相同 - 因此您不会获得异常。还有一个竞争条件,你最终可能会打电话给那些刚刚取消订阅的用户,但这是不可避免的。

答案 1 :(得分:4)

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

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

可以找到更多详细信息here

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:0)

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

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

但是,我建议总是将变体2作为最佳实践 - 这可能会让您在将来头疼; - )