1. 程式人生 > 其它 >dotnet 讀 WPF 原始碼筆記 AppDomainShutdownMonitor 的設計

dotnet 讀 WPF 原始碼筆記 AppDomainShutdownMonitor 的設計

技術標籤:javapythonandroidlinuxspring

本文是我在讀 WPF 原始碼做的筆記。在 WPF 中的 AppDomainShutdownMonitor 類是一個不開放的類,這個類當前只是給 D3DImage 類使用。在 AppDomainShutdownMonitor 提供了在應用的程序或程式域關閉的時候,進行一次通知,當前是用來清理 D3DImage 類的資源

在 WPF 中的 D3DImage 類是一個充滿黑科技的類,這個類因為黑科技而有這樣的要求,在程序退出或程式域關閉的時候,需要呼叫特別的邏輯進行釋放資源。同時在 D3DImage 類被回收的時候,就不需要訂閱程序退出或程式域關閉的時候的清理邏輯,因為在 D3DImage 回收的時候,將會自動執行清理邏輯

如果讓 D3DImage 類去關注程序退出等,那麼將會讓 D3DImage 類更加複雜,因此在 WPF 裡面加入了一個叫 IAppDomainShutdownListener 的介面,定義如下

    internal interface IAppDomainShutdownListener
    {
        void NotifyShutdown();
    }

這是一個不開放的介面,繼承這個介面的類可以獲得在 AppDomain 退出的時候的通知

為了實現這個介面的呼叫功能,在 WPF 中定義了靜態類 AppDomainShutdownMonitor 類,這個類裡面將會提供注入 IAppDomainShutdownListener 物件,在 AppDomain 退出的時候呼叫 IAppDomainShutdownListener 物件的 NotifyShutdown 方法,大概邏輯如下

        static AppDomainShutdownMonitor()
        {
            AppDomain.CurrentDomain.DomainUnload += OnShutdown;
            AppDomain.CurrentDomain.ProcessExit += OnShutdown;
        }

        public static void Add(IAppDomainShutdownListener listener)
        {
        	// 忽略程式碼
        }

        private static void OnShutdown(object sender, EventArgs e)
        {
        	// 忽略程式碼
        	listener.NotifyShutdown();
        }

如果只是這樣的實現將會存在問題,因為 AppDomainShutdownMonitor 是靜態類,如果在 Add 方法傳入的是物件,被 AppDomainShutdownMonitor 的靜態欄位儲存了,那麼將無法釋放 IAppDomainShutdownListener 物件。因此在 WPF 中的實際實現是採用一個 WeakReference 來實現

在當時的 WPF 開發的時候,還沒有 WeakReference<> 型別

更改之後的邏輯大概如下

        public static void Add(WeakReference listener)
        {
            Debug.Assert(listener.Target != null);
            Debug.Assert(listener.Target is IAppDomainShutdownListener);

            lock (_dictionary)
            {
                if (!_shuttingDown)
                {
                    _dictionary.Add(listener, listener);
                }
            }
        }

        private static Dictionary<WeakReference, WeakReference> _dictionary;

為什麼上面的存放 listener 物件的容器是 Dictionary 而不是 List 呢?因為還有這樣的需求,在 D3DImage 類被回收的時候,就不需要訂閱程序退出或程式域關閉的時候的清理邏輯,因此還有一個 Remove 方法

        public static void Remove(WeakReference listener)
        {
            Debug.Assert(listener.Target == null || listener.Target is IAppDomainShutdownListener);

            lock (_dictionary)
            {
                if (!_shuttingDown)
                {
                    _dictionary.Remove(listener);
                }
            }
        }

呼叫 Add 和 Remove 的程式碼分別如下

        public D3DImage(double dpiX, double dpiY)
        {
            // 忽略程式碼
            _listener = new WeakReference(this);
            AppDomainShutdownMonitor.Add(_listener);
        }

        ~D3DImage()
        {
            // 忽略程式碼
            AppDomainShutdownMonitor.Remove(_listener);
        }

為了能更快的呼叫 Remove 方法,也就將存放的容器設計為 Dictionary 了,但實際上沒有使用連結串列快,想不開的話,我會去優化一下這個邏輯

通過上面的邏輯,相信大家也瞭解到如何寫出在應用退出的時候的邏輯,以及編寫的時候可以參閱 WPF 的設計,儘管因為 WPF 寫這段邏輯的時候很多好用的特性還沒開發出來,但是需要稍微做一點改動,就可以用上新特性加上這個設計方式做到在應用退出的時候執行一些邏輯的清理

當前的 WPF 在 https://github.com/dotnet/wpf 完全開源,使用友好的 MIT 協議,意味著允許任何人任何組織和企業任意處置,包括使用,複製,修改,合併,發表,分發,再授權,或者銷售。在倉庫裡面包含了完全的構建邏輯,只需要本地的網路足夠好(因為需要下載一堆構建工具),即可進行本地構建

我搭建了自己的部落格 https://blog.lindexi.com/ 歡迎大家訪問,裡面有很多新的部落格。只有在我看到部落格寫成熟之後才會放在csdn或部落格園,但是一旦釋出了就不再更新

如果在部落格看到有任何不懂的,歡迎交流,我搭建了 dotnet 職業技術學院 歡迎大家加入

如有不方便在部落格評論的問題,可以加我 QQ 2844808902 交流

知識共享許可協議
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名林德熙(包含連結:http://blog.csdn.net/lindexi_gd ),不得用於商業目的,基於本文修改後的作品務必以相同的許可釋出。如有任何疑問,請與我聯絡。