Для начала пару слов, чтобы освежить память.

Dependency Property (Свойство зависимости) — это основа любого Silverlight/WPF/WP7 приложения. Их можно рассматривать как обычные свойства, но они обладают дополнительными возможностями, позволяющими работать с анимацией, привязкой данных, стилями. Также они эффективно потребляют память и поддерживают уведомления об изменениях.

Одной из основной черт Dependecy Property является приоретизация. Т.е. при присвоении им значения будет учтено несколько факторов. Наивысший приоритет будет иметь анимация, самый низкий – значения по умолчанию. Более подробно можно посмотреть на MSDN. В принципе, запоминать последовательность приоритетов не очень нужно — она и так интуитивно понятна.

Для того чтобы в классе можно было реализовывать Dependency Property необходимо, чтобы класс наследовался от DependencyObject либо его наследников, таких например как UIElement, Geometry, FrameworkTemplate, Style и других.

Напоминаю, как выглядит типична реализация Dependency Property:

public class MyStateControl : ButtonBase
{
    public MyStateControl() : base() { }
    public Boolean State
    {
        get { return (Boolean)this.GetValue(StateProperty); }
        set { this.SetValue(StateProperty, value); }
    }
    public static readonly DependencyProperty StateProperty =
        DependencyProperty.Register("State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false));
}

Реализация

Давайте посмотрим как DependencyProperty работают внутри. Первое, что я попытался сделать для этого, это использовать Reflector. К сожалению, в сборках для WP7 он не отобразил реализацию методов. Оказалось, что сборки,  которые лежат в c:Program Files (x86)Reference AssembliesMicrosoftFrameworkSilverlightv4.0ProfileWindowsPhone71 просто используются Visual Studi’ей для подсветки синтаксиса! В конце концов, натолкнулся на вот эту статью и закачал себе архив с дампом. Внутри дампа лежит не обфусцированая сборка GAC_System.Windows_v2_0_5_0_cneutral_1.dll, содержащая пространство имен System.Windows, в котором определены нужные нам объекты DependencyProperty и DependencyObject.

DependencyProperty

Итак, обратимся классу DependencyProperty:

namespace System.Windows
{
  public class DependencyProperty
  {
    public static readonly object UnsetValue;
    static DependencyProperty();
    public static DependencyProperty Register(string name, Type propertyType,
                            Type ownerType, PropertyMetadata typeMetadata);
    public static DependencyProperty RegisterAttached(string name, Type propertyType,
                            Type ownerType, PropertyMetadata defaultMetadata);
    public PropertyMetadata GetMetadata(Type forType);
  }
}

Нас интересуют методы Register и RegisterAttached. Оба они вызывают внутреннюю реализацию Register со следующей сигнатурой

internal static DependencyProperty Register(bool fIsAttachedDP, string name, Type propertyType, Type ownerType, PropertyMetadata propertyMetadata, bool readOnly)

В случае Register первым параметром (fIsAttachedDP) является false, а в случае RegisterAttached – true. Также передается имя свойства (name), его тип (propertyType), тип владельца (ownerType), флаг указывающий является ли свойство свойством только для чтения (readOnly) и экземпляр класса PropertyMetadata.

Сам по себе класс PropertyMetadata безумно простой. Он всего-навсего содержит два поля – значение по умолчание (defaultValue), и делегат, который вызывается, когда свойство меняет значение (propertyChangedCallback).

public class PropertyMetadata
{
     public object DefaultValue { get; private set; }
     internal PropertyChangedCallback PropertyChangedCallback { get; private set; }
}

Есть еще три конструктора, но они опущены для экономии места, поскольку интереса не представляют.

Взглянем на метод Register поподробнее

register

Его код можно условно разделить на три блока.

В первом в зависимости от переданного параметра fIsAttachedDP создается экземпляр класса CustomDependencyProperty либо CustomAttachedDependencyProperty, причем CustomAttachedDependencyProperty является наследником CustomDependencyProperty. В свою очередь CustomDependencyProperty наследуется непосредственно от DependencyProperty. Иерархия классов следующая:

hierarhy

Во втором блоке происходит простое копирование данных. Причем класс PropertyMetadata нигде дальше не используется — его поля просто-напросто копируются в DependencyProperty.

Наибольший интерес представляет собой третий блок, в котором вызывается метод RememberRegisteredPropery, ниже его реализация:

RememberRegisteredProperty

_registeredProperties – это статическое поле, представляющее собой словарь, ключом которого является тип владельца (ownerType), а значением еще один словарь, в котором ключ – имя регистрируемого свойства(name), а значение – непосредственно экземпляр этого свойства (dp). Изобразить это, например, можно вот так:

registeredProperty

Еще раз обращаю ваше внимание на то, что _registeredPropertiesявляется статическим (помечено как static) полем.

В следующем блоке формируется глобальный индекс свойства – m_KnownId. Заметчу, что m_KnownId – это поле класса DependencyProperty типа uint, к тожу же оно internal, то есть доступно другим классам в сборке.

К _registeredPropertiesи  m_KnownId мы еще вернемся при рассмотрении DependencyObject.  Но об этом, и о том за счет чего происходит экономия  памяти при использовании DependencyProperty читайте во второй части.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *