Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么我们应该使用临时对象来引发事件?_C#_.net_Wpf - Fatal编程技术网

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

C# 为什么我们应该使用临时对象来引发事件?,c#,.net,wpf,C#,.net,Wpf,大多数情况下,当我们使用MVVM时,我们使用INotifyPropertyChanged接口向绑定提供通知,一般实现如下所示: public class MyClass : INotifyPropertyChanged { // properties implementation with RaisePropertyChanged public event PropertyChangedEventHandler PropertyChanged; protected vo

大多数情况下,当我们使用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的答案和解释的例子:

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

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;
      }
}

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

在此代码中:

if (PropertyChanged != null)
{
    PropertyChanged(...);
}
空值检查后,
PropertyChanged
可能会变为
null
(由于最后一个订户取消订阅),这意味着您将获得一个
NullReferenceException


使用局部变量时,请确保检查为空的引用与引发事件时使用的引用相同,因此不会出现异常。仍然存在一种竞争条件,即您可能最终调用刚刚取消订阅的订阅者,但这是不可避免的。

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

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


可以找到更多详细信息

它仅在处理多线程场景时起作用:在
!=null
检查,实际调用可能会在代码1中遇到NullReferenceException

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


但是,我建议始终使用变体2作为最佳实践-这可能会在将来为您省去一个头痛;-)

出于线程安全的考虑,这是一种很好的做法

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


第二个示例消除了这种风险。

参见本文,参见Eric Lippert关于该主题的文章,谢谢Jon。这是最容易理解的条件。