WPF 的 Application.Current.Dispatcher 中,Dispatcher 属性一定不会为 null
原文:WPF 的 Application.Current.Dispatcher 中,Dispatcher 属性一定不会为 null
在 WPF 程序中,可能会存在 Application.Current.Dispatcher.Xxx 这样的代码让一部分逻辑回到主 UI 线程。因为发现在调用这句代码的时候出现了 NullReferenceException,于是就有三位小伙伴告诉我说 Current 和 Dispatcher 属性都可能为 null。
然而实际上这里只可能 Current 为 null 而此上下文的 Dispatcher 是绝对不会为 null 的。(当然我们这里讨论的是常规编程手段,如果非常规手段,你甚至可以让实例的 this 为 null 呢……)
由于本文所述的两个部分都略长,所以拆分成两篇博客,这样更容易理解。
- WPF 的 Application.Current.Dispatcher 中,Dispatcher 属性一定不会为 null
- WPF 的 Application.Current.Dispatcher 中,为什么 Current 可能为 null
Application.Dispatcher 实例属性
Application.Dispatcher 实例属性来自于 DispatcherObject。
源代码
为了分析此属性是否可能为 null,我现在将 DispatcherObject 的全部代码贴在下面:
using System;
using System.Windows;
using System.Threading;
using MS.Internal.WindowsBase; // FriendAccessAllowed
namespace System.Windows.Threading
{
/// <summary>
/// A DispatcherObject is an object associated with a
/// <see cref="Dispatcher"/>. A DispatcherObject instance should
/// only be access by the dispatcher's thread.
/// </summary>
/// <remarks>
/// Subclasses of <see cref="DispatcherObject"/> should enforce thread
/// safety by calling <see cref="VerifyAccess"/> on all their public
/// methods to ensure the calling thread is the appropriate thread.
/// <para/>
/// DispatcherObject cannot be independently instantiated; that is,
/// all constructors are protected.
/// </remarks>
public abstract class DispatcherObject
{
/// <summary>
/// Returns the <see cref="Dispatcher"/> that this
/// <see cref="DispatcherObject"/> is associated with.
/// </summary>
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Advanced)]
public Dispatcher Dispatcher
{
get
{
// This property is free-threaded.
return _dispatcher;
}
}
// This method allows certain derived classes to break the dispatcher affinity
// of our objects.
[FriendAccessAllowed] // Built into Base, also used by Framework.
internal void DetachFromDispatcher()
{
_dispatcher = null;
}
// Make this object a "sentinel" - it can be used in equality tests, but should
// not be used in any other way. To enforce this and catch bugs, use a special
// sentinel dispatcher, so that calls to CheckAccess and VerifyAccess will
// fail; this will catch most accidental uses of the sentinel.
[FriendAccessAllowed] // Built into Base, also used by Framework.
internal void MakeSentinel()
{
_dispatcher = EnsureSentinelDispatcher();
}
private static Dispatcher EnsureSentinelDispatcher()
{
if (_sentinelDispatcher == null)
{
// lazy creation - the first thread reaching here creates the sentinel
// dispatcher, all other threads use it.
Dispatcher sentinelDispatcher = new Dispatcher(isSentinel:true);
Interlocked.CompareExchange<Dispatcher>(ref _sentinelDispatcher, sentinelDispatcher, null);
}
return _sentinelDispatcher;
}
/// <summary>
/// Checks that the calling thread has access to this object.
/// </summary>
/// <remarks>
/// Only the dispatcher thread may access DispatcherObjects.
/// <p/>
/// This method is public so that any thread can probe to
/// see if it has access to the DispatcherObject.
/// </remarks>
/// <returns>
/// True if the calling thread has access to this object.
/// </returns>
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public bool CheckAccess()
{
// This method is free-threaded.
bool accessAllowed = true;
Dispatcher dispatcher = _dispatcher;
// Note: a DispatcherObject that is not associated with a
// dispatcher is considered to be free-threaded.
if(dispatcher != null)
{
accessAllowed = dispatcher.CheckAccess();
}
return accessAllowed;
}
/// <summary>
/// Verifies that the calling thread has access to this object.
/// </summary>
/// <remarks>
/// Only the dispatcher thread may access DispatcherObjects.
/// <p/>
/// This method is public so that derived classes can probe to
/// see if the calling thread has access to itself.
/// </remarks>
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public void VerifyAccess()
{
// This method is free-threaded.
Dispatcher dispatcher = _dispatcher;
// Note: a DispatcherObject that is not associated with a
// dispatcher is considered to be free-threaded.
if(dispatcher != null)
{
dispatcher.VerifyAccess();
}
}
/// <summary>
/// Instantiate this object associated with the current Dispatcher.
/// </summary>
protected DispatcherObject()
{
_dispatcher = Dispatcher.CurrentDispatcher;
}
private Dispatcher _dispatcher;
private static Dispatcher _sentinelDispatcher;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
代码来自:DispatcherObject.cs。
Dispatcher 属性仅仅是在获取 _dispatcher 字段的值,因此我们只需要看 _dispatcher 字段的赋值时机,以及所有给 _dispatcher 赋值的代码。
由于 _dispatcher 字段是私有字段,所以仅需调查这个类本身即可找到所有的赋值时机。(反射等非常规手段需要排除在外,因为这意味着开发者是逗比——自己闯的祸不能怪 WPF 框架。)
赋值时机
先来看看 dispatcher 字段的赋值时机。
DispatcherObject 仅有一个构造函数,而这个构造函数中就已经给 _dispatcher 赋值了,因此其所有的子类的初始化之前,_dispatcher 就会被赋值。
protected DispatcherObject()
{
_dispatcher = Dispatcher.CurrentDispatcher;
}
- 1
- 2
- 3
- 4
那么所赋的值是否可能为 null 呢,这就要看 Dispatcher.CurrentDispatcher 是否可能返回一个 null 了。
以下是 Dispatcher.CurrentDispatcher 的属性获取代码:
public static Dispatcher CurrentDispatcher
{
get
{
// Find the dispatcher for this thread.
Dispatcher currentDispatcher = FromThread(Thread.CurrentThread);;
// Auto-create the dispatcher if there is no dispatcher for
// this thread (if we are allowed to).
if(currentDispatcher == null)
{
currentDispatcher = new Dispatcher();
}
return currentDispatcher;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
可以看到,无论前面的方法得到的值是否是 null,后面都会再给 currentDispatcher 局部变量赋值一个新创建的实例的。因此,此属性是绝对不会返回 null 的。
由此可知,DispatcherObject 自构造起便拥有一个不为 null 的 Dispatcher 属性,其所有子类在初始化之前便会得到不为 null 的 Dispatcher 属性。
后续赋值
现在我们来看看在初始化完成之后,后面是否有可能将 _dispatcher 赋值为 null。
给 _dispatcher 字段的赋值代码仅有两个:
// This method allows certain derived classes to break the dispatcher affinity
// of our objects.
[FriendAccessAllowed] // Built into Base, also used by Framework.
internal void DetachFromDispatcher()
{
_dispatcher = null;
}
// Make this object a "sentinel" - it can be used in equality tests, but should
// not be used in any other way. To enforce this and catch bugs, use a special
// sentinel dispatcher, so that calls to CheckAccess and VerifyAccess will
// fail; this will catch most accidental uses of the sentinel.
[FriendAccessAllowed] // Built into Base, also used by Framework.
internal void MakeSentinel()
{
_dispatcher = EnsureSentinelDispatcher();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
第一个 DetachFromDispatcher 很好理解,让 DispatcherObject 跟 Dispatcher 无关。在整个 WPF 的代码中,使用此方法的仅有以下 6 处:
Freezable.Freeze实例方法BeginStoryboard.Seal实例方法Style.Seal实例方法TriggerBase.Seal实例方法StyleHelper在SealTemplate静态方法中对FrameworkTemplate类型的实例调用此方法ResourceDictionary在构造函数中为DispatcherObject类型的DummyInheritanceContext属性调用此方法
而 Application 类型不是以上任何一个类型的子类(Application 类的直接基类是 DispatcherObject),因此 Application 类中的 Dispatcher 属性不可能因为 DetachFromDispatcher 方法的调用而被赋值为 null。
接下来看看 MakeSentinel 方法,此方法的作用不如上面方法那样直观,实际上它的作用仅仅为了验证某个方法调用时所在的线程是否是符合预期的(给 VerifyAccess 和 CheckAccess 使用)。
使用此方法的仅有 1 处:
ItemsControl所用的ItemInfo类的静态构造函数
internal static readonly DependencyObject SentinelContainer = new DependencyObject();
internal static readonly DependencyObject UnresolvedContainer = new DependencyObject();
internal static readonly DependencyObject KeyContainer = new DependencyObject();
internal static readonly DependencyObject RemovedContainer = new DependencyObject();
static ItemInfo()
{
// mark the special DOs as sentinels. This helps catch bugs involving
// using them accidentally for anything besides equality comparison.
SentinelContainer.MakeSentinel();
UnresolvedContainer.MakeSentinel();
KeyContainer.MakeSentinel();
RemovedContainer.MakeSentinel();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
所有这些使用都与 Application 无关。
结论
总结以上所有的分析:
Application类型的实例在初始化之前,Dispatcher属性就已经被赋值且不为null;- 所有可能改变
_dispatcher属性的常规方法均与Application类型无关;
因此,所有常规手段均不会让 Application 类的 Dispatcher 属性拿到 null 值。如果你还说拿到了 null,那就检查是否有逗比程序员通过反射或其他手段将 _dispatcher 字段改为了 null 吧……
Application.Current 静态属性
关于 Application.Current 是否可能为 null 的分析,由于比较长,请参见我的另一篇博客:
参考资料
我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。
如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。
WPF 的 Application.Current.Dispatcher 中,Dispatcher 属性一定不会为 null的更多相关文章
- WPF 的 Application.Current.Dispatcher 中,为什么 Current 可能为 null
原文:WPF 的 Application.Current.Dispatcher 中,为什么 Current 可能为 null 在 WPF 程序中,可能会存在 Application.Current.D ...
- spring boot 读取配置文件(application.yml)中的属性值
在spring boot中,简单几步,读取配置文件(application.yml)中各种不同类型的属性值: 1.引入依赖: <!-- 支持 @ConfigurationProperties 注 ...
- WPF中Application.Current的使用
WPF程序对应一个Application对象,当前的Application对象可以通过Application.Current获取,通过获取到的Application对象,我们可以做以下的事情: App ...
- 2018-8-10-win10-uwp-Window.Current.Dispatcher中Current为null
title author date CreateTime categories win10 uwp Window.Current.Dispatcher中Current为null lindexi 201 ...
- System.Windows.Application.Current.Dispatcher.BeginInvoke
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => ...
- Dispatcher中Invoke与BeginInvoke
[同步]Invoke Application.Current.Dispatcher.Invoke(AutoIncreaseNumber); [异步]BeginInvoke Application.Cu ...
- WPF:如何高速更新Model中的属性
原文:[WPF/MVVM] How to deal with fast changing properties In this article, I will describe a problem w ...
- Implement Property Value Validation in the Application Model 在应用程序模型中实现属性值验证
In this lesson, you will learn how to check whether or not a property value satisfies a particular r ...
- struts2中访问和添加Application、session以及request属性
一.访问或添加Application.session.request属性 <一>方式一 HelloWorldAction类中添加如下代码 //此方法适用于仅对Application.ses ...
随机推荐
- vue+element 表格导出Excel文件
https://www.cnblogs.com/bobodeboke/p/8867481.html 非常感谢 这个大佬 才让我搞到了Blob.js 和 Export2Excel.js 如果最后运行时 ...
- centos硬件查询
1.cpu个数: [root@localhost ~]# cat /proc/cpuinfo |grep "physical id"|sort|uniq|wc -lcpu核心数: ...
- python 播放MP3和MP4
import pygame import time def play_music(): filepath = r"900A.mp3"; pygame.mixer.init() # ...
- SQL Delta实用案例介绍
概述 本篇文章主要介绍SQL DELTA的简单使用.为了能够更加明了的说明其功能,本文将通过实际项目中的案例加以介绍. 主要容 SQL DELTA 简介 创建SQL DELTA项目 ...
- SpringBoot使用jasypt加解密密码
在我们的服务中不可避免的需要使用到一些秘钥(数据库.redis等) 开发和测试环境还好,但生产如果采用明文配置讲会有安全问题,jasypt是一个通用的加解密库,我们可以使用它. <depende ...
- 一款阿里开源的 Java 诊断工具
Arthas是什么鬼? Arthas是一款阿里巴巴开源的 Java 线上诊断工具,功能非常强大,可以解决很多线上不方便解决的问题. Arthas诊断使用的是命令行交互模式,支持JDK6+,Linux. ...
- electron---项目打包
创建一个应用目录:app,里面需要有必要的三个文件: index.html <!DOCTYPE html> <html> <head> <meta chars ...
- python操作excel——openpyxl
一.概述 python操作excel各个库对比:https://www.cnblogs.com/paul-liang/p/9187503.html 官方文档:https://openpyxl.read ...
- 一秒解决CentOS下service 功能 不能使用 bash: service: command not found
首先检查自己是否 使用的是root用户 如果是并且还不能用-----执行以下操作 在centos系统中,如果/sbin目录下没有service这个命令,就会出现 bash: service: comm ...
- depth wise CNN
depth wise cnn相对于传统的CNN,区别在于:它是逐通道做卷积操作! 例子如下: (1)使用传统卷积,输入:H*W*C_in,最终输出h*w*C_out:卷积核尺寸为K*K*C_in*C_ ...
