Моя ментальная модель работы свойств зависимости:
Любой класс DependencyObject
реализует два специальных свойства. Одним из них, статическим свойством класса, является словарь из DependencyProperty
объектов. Каждый экземпляр класса может заглянуть внутрь этого словаря, чтобы найти метаинформацию о каждом DependencyProperty
- имя свойства, его тип, любые обратные вызовы, которые должны вызываться при его получении и установке, как оно участвует в наследовании свойства и так далее. Когда вы регистрируете свойство зависимостей, вы добавляете запись в этот словарь.
Другое свойство является свойством экземпляра: это словарь с ключом DependencyProperty
, который содержит локальное значение каждого DependencyProperty
, если оно установлено.
Методы SetValue
и GetValue
, которые вы реализуете в установщике и получателе свойства CLR, являются в основном ленивой оценкой на стероидах. Вместо сохранения и извлечения значения свойства в вспомогательном поле они сохраняют и извлекают значение свойства в словаре значений.
Волшебство свойств зависимости заключается в том, что на самом деле делают GetValue
и SetValue
.
GetValue
ищет значение свойства в словаре значений объекта. Если он не находит его, он вызывает GetValue
для родительского элемента, чтобы получить то, что родительский элемент считает значением. Например, когда вы создаете TextBox
в Window
, все, что смотрит на TextBox
FontFamily
, на самом деле вызывает GetValue
. Если вы явно не установили шрифт, в его словаре нет записи для этого свойства. Поэтому GetValue
запрашивает значение у родительского элемента. Родительский элемент может иметь или не иметь FontFamily
set; если нет, его вызовет GetValue
, чтобы вернуть значение от его родителя. И так до тех пор, пока не будет достигнут объект Window
и не будет найдено действительное значение FontFamily
.
Если вы установите FontFamily
на TextBox
, SetValue
сохранит значение в словаре значений. В следующий раз, когда что-либо понадобится получить значение FontFamily
для этого TextBox
, GetValue
найдет значение в словаре и вернет его, поэтому ему не нужно запрашивать родительский элемент.
Если вы установите FontFamily
в Window
, SetValue
не только обновляет значение в словаре значений Window
, оно запускает событие изменения свойства, которое слышит все, что зависит от свойства. (Вот почему они называются свойствами зависимости, помните.) И если вещь, зависящая от свойства, сама является свойством зависимости, она запускает свои собственные события изменения свойства. Таким образом, изменение FontFamily
на Window
изменяет шрифт для каждого элемента управления в окне, а также побуждает WPF повторно визуализировать измененные элементы управления.
Прикрепленные свойства работают с использованием такого же подхода. Любой объект, который может иметь вложенные свойства, имеет словарь, в котором хранятся значения вложенных свойств. Когда вы устанавливаете Grid.Column
для CheckBox
в XAML, вы просто добавляете запись в словарь этого CheckBox
. Когда Grid
нужно знать, в каком столбце находится CheckBox
, он ищет значение из этого словаря. Когда для объекта Grid.IsSharedSizeScope
установлено значение True
, словарь этого объекта будет содержать новое свойство - словарь, содержащий ширину / высоту для каждого SharedSizeKey
.
Я должен подчеркнуть, что это моя ментальная модель. Я не сел с Reflector и посмотрел на фактическую реализацию Register
, GetValue
и SetValue
, чтобы выяснить, как они на самом деле работают. Я могу ошибаться в деталях. Но это модель, которая точно предсказывает, как эти вещи ведут себя, так что это достаточно хорошо.
Концепция хранения значений свойств в словарях довольно странна для программистов на C #. Это старая шляпа для программистов на Python. В Python все свойства класса - фактически все объекты - хранятся в словарях, и поэтому вы можете получить их значение либо через средства доступа к свойствам, либо просто просмотрев их. Свойства зависимостей и вложенные свойства - это еще один способ, которым .NET, украдя все, что было в Java, что стоило украсть, теперь грабит Python. (Или откуда бы Python не грабил их.) Изучение Python сделало меня намного лучшим программистом на C #; Я рекомендую это любому разработчику C #, который еще не сделал этого.