在学习WPF的过程中,依赖属性(有些教程称之为“依赖项属性,我喜欢称之为”依赖属性)的概念一直搞不明白,实在不明白微软为何要引入这么个难懂的东西。
然后翻书,逛帖子,看源码,总算弄懂了一点了。
先贴出资源吧:
微软DotNet源码(官方的哦,不是我反编译的):Reference Source (microsoft.com)
东邪西毒的博客:继续聊WPF——依赖项属性(1)
以下是我自己写的,作个笔记:
依赖属性的形式:
// 这里只使用三个参数,实际上可以有五个参数
public static readonly DependencyProperty MarginProperty = DependencyProperty.Register("Margin", typeof(Thickness), _typeofThis);
// 以传统属性的形式,包装依赖属性(MarginProperty),在外部代码看来,它就是个普通的属性。
public Thickness Margin
{
get { return (Thickness) GetValue(MarginProperty); }
set { SetValue(MarginProperty, value); }
}
依赖属性的类型是DependencyProperty,在这个类中,有两个关键的成员:
public int GlobalIndex{get;}
private static Hashtable PropertyFromName;
GlobalIndex:是一个只读的实例属性,作为DependencyProperty对象的全局的且唯一的标识。
PropertyFromName:是一个静态的Hashtable。因此,在DependencyProperty类载入内存的时候,就会创建全局的,唯一的对象(PropertyFromName)。它类似于Dictionary,是一个键值对。key,使用”属性名“ 和”ownerType”的hash值计算而来,value就是依赖属性的对象。因此,PropertyFromName,只是用来保存程序集中所有的依赖随性对象。
备注:依赖属性的类型是DependencyProperty。实际上它只是包装了真实的数据类型。比如Margin属性的真实类型是Thickness结构,这在注册属性的时候需要指明。因此在设置值时,可以使用真实的数据类型,或者派生的数据类型。
所有的控件、元素,均继承自DependencyObject。它有一个关键字段:
private EffectiveValueEntry[] _effectiveValues
EffectiveValueEntry类型,有三个字段:
Value —— 它表示依赖属性的对象(object类型)
PropertyIndex —— 它标识依赖属性的索引,它的值就是DependencyProperty对象的GlobalIndex。
FullValueSource —— 它表示依赖属性值的来源。因为属性值可以是本地设置的,也可以是继承自父级元素的,也可以是来自动画、模板、样式的。
由此可见,EffectiveValues采用索引——值的形式,保存的是依赖属性的设置的值。所以他们的关系是:
所以,依赖属性的工作方式如下:
- DependencyProperty类载入内存,并创建PropertyFromName对象(HashTable)。
- 创建依赖属性,并注册。此时DependencyProperty的静态字段PropertyFromName便保存了所有依赖属性的原始值(默认值)
- 初始化DependencyObject对象(如控件、元素等),并创建数组EffectiveValueEntry空数组(此步操作用xaml语言完成,该数组由对象持有);
- 读取属性值(使用DependencyObject的GetValue() 方法):首先查找本地,如果没有,就找出继承,动画、样式等,都没有找到,就到PropertyFromName中查找默认值
- 设置值(使用DependencyObject的SetValue() 方法,将值保存在EffectiveValueEntry数组中。
为何要如此设计:
我的理解:
- 可以减少内存消耗。如果使用实例属性,则每个对象都会初始化N个属性,但是这些属性并不一定都使用,则空值属性占用了很多内存。依赖属性,以静态字段的方式,保存默认值,以“依赖”的方式,获得属性值,只有在明确设定属性值时才在EffectiveValueEntry数组中添加索引——属性值的映射,因此可以减少内存的占用。
- 根据不同的环境,获取最合适的值。因为WPF应用了样式、模板、动画等功能,这些都可以改变属性的值。如果使用传统属性,则这些操作都要去设置属性的值,系统消耗比较大。而以依赖属性的方式,则只有在明确设置、读取属性值的时候,才会操作属性,如此可以降低系统开销。