admin管理员组

文章数量:1530517

在 WPF 程序中,可能会存在 Application.Current.Dispatcher.Xxx 这样的代码让一部分逻辑回到主 UI 线程。因为发现在调用这句代码的时候出现了 NullReferenceException,于是就有三位小伙伴告诉我说 CurrentDispatcher 属性都可能为 null

然而实际上这里只可能 Currentnull 而此上下文的 Dispatcher 是绝对不会为 null 的。(当然我们这里讨论的是常规编程手段,如果非常规手段,你甚至可以让实例的 thisnull 呢……)


当你的应用程序退出时,所有 UI 线程的代码都不再会执行,因此这是安全的;但所有非 UI 线程的代码依然在继续执行,此时随时可能遇到 Application.Current 属性为 null。

由于本文所述的两个部分都略长,所以拆分成两篇博客,这样更容易理解。

  • WPF 的 Application.Current.Dispatcher 中,Dispatcher 属性一定不会为 null
  • WPF 的 Application.Current.Dispatcher 中,为什么 Current 可能为 null

本文内容

    • `Application.Current` 静态属性
      • 源代码
      • 赋值时机
      • 后续赋值
      • 对所写代码的影响
    • 与 `Application.Current` 不在同一线程
        • 使用 `Invoke/BeginInvoke/InvokeAsync` 的代码不会出问题
      • 最简示例代码
      • 结论
    • `Application.Dispatcher` 实例属性

Application.Current 静态属性

源代码

Application 类型的源代码会非常长,所以这里就不贴了,可以前往这里查看:

  • DispatcherObject.cs

其中,Current 返回的是 _appInstance 的静态字段。因此 _appInstance 字段为 null 的时机就是 Application.Currentnull 的时机。

/// <summary>
///     The Current property enables the developer to always get to the application in
///     AppDomain in which they are running.
/// </summary>
static public Application Current
{
   
    get
    {
   
        // There is no need to take the _globalLock because reading a
        // reference is an atomic operation. Moreover taking a lock
        // also causes risk of re-entrancy because it pumps messages.

        return _appInstance;
    }
}

由于 _appInstance 字段是私有字段,所以仅需调查这个类本身即可找到所有的赋值时机。(反射等非常规手段需要排除在外,因为这意味着开发者是逗比——自己闯的祸不能怪 WPF 框架。)

赋值时机

_appInstance 的赋值时机有两处:

  1. Application 的实例构造函数(注意哦,是实例构造函数而不是静态构造函数);
  2. Application.DoShutdown 方法。

Application 的实例构造函数中:

  • _appInstance 的赋值是线程安全的,这意味着多个 Application 实例的构造不会因为线程安全问题导致 _appInstance 字段的状态不正确。
  • 如果 _appCreatedInThisAppDomaintrue 那么,将抛出异常,组织此应用程序域中创建第二个 Application 类型的实例。
/// <summary>
///     Application constructor
/// </summary>
/// <SecurityNote>
///    Critical: This code posts a work item to start dispatcher if in the browser
///    PublicOk: It is ok because the call itself is not exposed and the application object does this internally.
/// </SecurityNote>
[SecurityCritical]
public Application()
{
   
    // 省略了一部分代码。
    lock(_globalLock)
    {
   
        // set the default statics
        // DO NOT move this from the begining of this constructor
        if (_appCreatedInThisAppDomain == false)
        {
   
            Debug.Assert(_appInstance == null, "_appInstance must be null here.");
            _appInstance = this

本文标签: 能为applicationWPFCurrentnull