Примечание: если вы не любите читать, или у вас нет времени, читайте сразу с абзаца с заголовком "Итак, суть такова"
Погружаясь в глубины свойств зависимости в WPF, решил узнать, как создатели пишут свойства зависимости для стандартных компонентов. Открыл dotPeek, в нём открыл PresentationCore.dll, PresentationFramework.dll и WindowsBase.dll и - вперёд!
Решил начать с малого, посмотреть реализацию кнопки (Button). Читаю код, вроде бы всё понятно, но вот в строке 9:
C# | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
| public class Button : ButtonBase
{
public static readonly DependencyProperty IsDefaultProperty = DependencyProperty.Register(
"IsDefault", // Имя свойства
typeof(bool), // Тип данных свойства
typeof(Button), // Тип-владелец свойства
// Здесь настраиваем свойство
(PropertyMetadata)new FrameworkPropertyMetadata(
BooleanBoxes.FalseBox, // Значение по-умолчанию. Что это за BooleanBoxes?
// Обратный вызов при изменении свойства
new PropertyChangedCallback(Button.OnIsDefaultChanged))
);
// ...
} |
|
Что такое BooleanBoxes.FalseBox? Жмём F12 и смотрим определение
C# | 1
2
3
4
5
6
7
8
9
10
11
12
| using MS.Internal.WindowsBase;
namespace MS.Internal.KnownBoxes
{
[FriendAccessAllowed]
internal static class BooleanBoxes
{
internal static object TrueBox = (object)true;
internal static object FalseBox = (object)false;
}
// ...
} |
|
"Хм. Зачем писать такой велосипед, чтобы просто передать true или false?" - задаю я вопрос сам себе.
Читаем дальше, все остальные определения в том же духе. Смотрим оболочку для свойства IsDefaultProperty:
C# | 1
2
3
4
5
6
7
8
| public bool IsDefault {
get {
return (bool)this.GetValue(Button.IsDefaultProperty);
}
set {
this.SetValue(Button.IsDefaultProperty, BooleanBoxes.Box(value));
}
} |
|
Опять BooleanBoxes! И снова возвращает true или false!
C# | 1
2
3
4
5
6
| internal static object Box(bool value) {
if (value)
return BooleanBoxes.TrueBox;
else
return BooleanBoxes.FalseBox;
} |
|
Тогда я стал искать причину появления такого интересного кода в сети (в это время перебирал варианты: "Может индусы постарались" или "Может dotPeek заморачивается"). И нашёл. Ответ на мои мыслительные терзания был в статейке одного из бесконечных блогов по .NET.
Итак, суть такова
Свойства зависимости не обобщённые и работают с типом System.Object, приводя его к нужному типу при использовании. Если мы будем использовать в свойстве значимый тип (value type), то его приведение к Object и обратно будет вызывать операцию упаковки/распаковки (box/unbox), которая, как известно не особо расторопная. А так как платформа огромная, мы получим в результате тонны упаковок/распаковок, что будет серьёзным ударом по производительности.
Поэтому разработчики платформы заранее "упаковали" некоторые часто используемые значимые типы и, с помощью метода Box() обращались к ним. Майкрософтовцами были "избраны" следующие типы:- struct System.Boolean -> BooleanBoxes
- enum System.Windows.Media.FillRule -> FillRuleBoxes
- enum System.Windows.Visibility -> VisibilityBoxes
- struct System.Windows.Size -> SizeBox
"Упакованные" версии этих типов располагаются в пространстве имён MS.Internal.KnownBoxes.
На фоне всех преимуществ заранее упакованных типов, остаётся только гадать, почему разработчики упрятали их в глубины своего кода. И почему бы не поместить их в какое-нибудь видимое общедоступное пространство имён?
Я буду рад вашим комментариям и замечаниям. Спасибо, что прочли это. |