在 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 協議,意味着允許任何人任何組織和企業任意處置,包括使用,複製,修改,合併,發表,分發,再授權,或者銷售。在倉庫裏面包含了完全的構建邏輯,只需要本地的網絡足夠好(因爲需要下載一堆構建工具),即可進行本地構建