为什么我们应该使用临时对象来引发事件
本文关键字:事件 临时对象 我们 为什么 | 更新日期: 2023-09-27 18:20:46
大多数时候,当我们使用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的答案和解释的例子的答案:
理解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;
}
}
您没有创建临时对象。您正在使用局部变量来避免竞争条件。
在此代码中:
if (PropertyChanged != null)
{
PropertyChanged(...);
}
在无效性检查之后,PropertyChanged
有可能变成null
(由于最后一个订户取消订阅),这意味着您将获得NullReferenceException
。
当您使用局部变量时,您要确保您检查是否为null的引用与您用于引发事件的引用相同,这样您就不会得到异常。仍然有一个竞争条件,你可能会给刚刚取消订阅的用户打电话,但这是不可避免的。
这是为了避免在检查null(查看是否附加了任何事件处理程序)和调用事件之间从事件中删除最后一个(或唯一一个)事件处理程序的罕见情况。如果发生这种情况,你会得到一个NullReferenceException
。
如果您担心内存泄漏——不要担心——它只是一个引用,而不是事件处理程序的副本。
更多详细信息可以在这里找到
出于线程安全的考虑,这是一个很好的做法。
在原始代码中,理论上,单独的线程可以在if
语句之后删除PropertyChanged
处理程序,但在事件之前删除。这将导致NullReferenceException
。
第二个样本消除了这种风险。
只有在处理多线程场景时才有区别:当最后一个事件处理程序在!= null
检查后注销时,实际调用可能会在代码1中遇到NullReferenceException。
然而,代码2没有这个问题,因为委托(事件背后的概念)是不可变的,因此临时变量的值不能更改。
然而,我建议始终使用变体2作为最佳实践-这可能会在未来省去你的头痛;-)