dotnet 讀 WPF 源代碼筆記 爲什麼加上 BooleanBoxes 類

在 WPF 框架,爲什麼需要定義一個 BooleanBoxes 類。爲什麼在 D3DImage 的 Callback 方法裏面,傳入的是 object 對象,卻能被轉換爲布爾。本文將告訴大家爲什麼需要這樣設計

大家都知道,在 dotnet 裏面,如果將一個結構體通過 object 的方式傳輸,將需要進行裝箱。而裝箱將會創建一個新的對象。在 WPF 這個框架裏面,有很多邏輯,例如消息,都是非常快速在調用的。如果每次調用,例如傳輸布爾值,由於需要進入很多框架邏輯,而讓參數只能使用 object 類型,那麼每次都使用結構體將需要多次的裝箱,從而創建大量的對象

創建大量的對象將會讓界面邏輯需要不斷進行內存回收,自然性能就降低了

那爲什麼不設計一個泛形呢?因爲代碼將不好寫,同時由於泛形類型的靜態屬性將不相同,從而再次讓邏輯更加複雜。而且對於大多數邏輯來說,確實傳輸的只是引用對象,傳輸結構體還是一個比較少的業務。在 WPF 框架,爲了解決此問題,於是就創建了 KnownBoxes 系列類型。包括 NullableBooleanBoxes 和 BooleanBoxes 類型。這兩個類型將預先將布爾裝箱,當成 object 對象。接下來,所有需要對布爾裝箱的邏輯,都將使用 BooleanBoxes 的對象代替

以下代碼是 BooleanBoxes 的邏輯

    internal static class BooleanBoxes
    {
        internal static object TrueBox = true;
        internal static object FalseBox = false;

        internal static object Box(bool value)
        {
            if (value)
            {
                return TrueBox;
            }
            else
            {
                return FalseBox;
            }
        }
    }

可以看到 BooleanBoxes 的 TrueBox 和 FalseBox 屬性都是由布爾裝箱創建的。爲什麼創建的方法是需要使用布爾裝箱,而不是隨便拿兩個對象?原因是如此方便重新轉換爲布爾值

使用 BooleanBoxes 的性能如何?請看 https://github.com/dotnet/runtime/issues/7079#issuecomment-264500921

Method Mean StdDev Median Scaled
BoolUncachedBoxing 7.3923 ns 0.0391 ns 7.3866 ns 1.00
BoolCachedBoxing 4.5859 ns 0.0310 ns 4.5954 ns 0.62

那爲什麼在 dotnet 裏面,不默認加上此優化呢?原因是如文檔,每次在 dotnet 的裝箱,都是生成新的對象。沒錯,新的對象。因此如果做此優化,將修改行爲

那這和 D3DImage 的 Callback 方法裏面,有什麼關係呢?其實在此方法裏面,調用到 SetIsFrontBufferAvailable 方法,在此方法裏面進行了一次強轉,於是我開始閱讀代碼,認爲強轉會炸,先來看看此方法做了什麼

        private object SetIsFrontBufferAvailable(object isAvailableVersionPair)
        {
            Pair pair = (Pair)isAvailableVersionPair;
            uint version = (uint)pair.Second;

            if (version == _version)
            {
                bool isFrontBufferAvailable = (bool)pair.First;
                SetValue(IsFrontBufferAvailablePropertyKey, isFrontBufferAvailable);
            }

            // ...just because DispatcherOperationCallback requires returning an object
            return null;
        }

此方法的參數能拿到一個 Pair 類型的對象,然而此對象的兩個值都是 object 類型,需要進行一次轉換。然而在 Callback 方法裏面,代碼如下

        private void Callback(bool isFrontBufferAvailable, uint version)
        {
            Dispatcher.BeginInvoke(
                DispatcherPriority.Normal,
                new DispatcherOperationCallback(SetIsFrontBufferAvailable),
                new Pair(BooleanBoxes.Box(isFrontBufferAvailable), version)
                );
        }

可以看到在傳入的參數,拿到的 Pair 的第一個參數,是用 BooleanBoxes 創建的。然而在 SetIsFrontBufferAvailable 方法裏面,將此參數進行了強轉。相當於 (bool) BooleanBoxes.Box(isFrontBufferAvailable) 的代碼。我開始看到 BooleanBoxes 的 Box 返回的是一個 object 對象,以爲對 object 對象進行強轉肯定會炸。實際上這是不會炸的,轉換是符合預期的

那爲什麼一個 object 對象,在 SetIsFrontBufferAvailable 能被轉換爲布爾呢?這就是 BooleanBoxes 的屬性都是由布爾裝箱創建的原因。因爲本來是通過布爾裝箱創建的,也因此能被轉換爲布爾值

以上就是 WPF 爲什麼加上 BooleanBoxes 類的原因,以及在 D3DImage 裏,使用布爾強轉一個 object 可以符合預期

更多邏輯,還請閱讀 WPF 源代碼

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章