依赖属性解析

Windows Presentation Foundation (WPF) 提供了一组服务,这些服务可用于扩展公共语言运行时 (CLR) 属性的功能,这些服务通常统称为 WPF 属性系统。由 WPF 属性系统支持的属性称为依赖项属性,虽然不清楚依赖属性,但是属性我们是很清楚的,封装类的字段,表示类的状态,编译后被转化为get_set_方法,可以被类或结构等使用,常见的一个属性如下:

1
2
3
4
5
6
7
8
9
public class ClassObject
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
}

既然已经有了属性,为什么还要有依赖属性呢?必然是属性有一些缺点了,而依赖属性恰好能够解决这个问题。以 Button 为例:

每次继承,父类的私有字段都被继承下来。对 Button 来说,大多数属性并没有被修改,仍然保持父类定义时的默认值。通常情况下,在整个 Button 的对象生命周期中,也只有少部分属性被修改。也已看得出来:

每次继承,子类对象都会获得更多的属性,这样,继承树的低端对象不可避免的膨胀;
既然大多数属性没有被修改,那么就可以把这些属性从对象中剥离,减少对象的体积;
可以知道,依赖属性就是为了解决这个问题诞生了。首先定义依赖属性,它里面存储之前 2 中希望剥离的属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class DependencyProperty
{
// 维护了一个全局的Map用来储存所有的DP
internal static Dictionary<object, DependencyProperty> RegisteredDps = new Dictionary<object, DependencyProperty>();
internal string Name;//注册属性名称
internal object Value;//属性值
internal object HashCode;//Mape唯一键值

private DependencyProperty(string name, Type propertyName, Type ownerType, object defaultValue)
{
this.Name = name;
this.Value = defaultValue;
this.HashCode = name.GetHashCode() ^ ownerType.GetHashCode();
}
// 对外暴露一个Register方法用来注册新的DP
public static DependencyProperty Register(string name, Type propertyName, Type ownerType, object defaultValue)
{
DependencyProperty dp = new DependencyProperty(name, propertyName, ownerType, defaultValue);
RegisteredDps.Add(dp.HashCode, dp);
return dp;
}
}

然后定义 DependencyObject 来使用 DP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class DependencyObject
{
// 注册一个新的DP:NameProperty
public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(DependencyObject), string.Empty);

public object GetValue(DependencyProperty dp)
{
return DependencyProperty.RegisteredDps[dp.HashCode].Value;
}

public void SetValue(DependencyProperty dp, object value)
{
DependencyProperty.RegisteredDps[dp.HashCode].Value = value;
}

public string Name
{
get
{
return (string)GetValue(NameProperty);
}
set
{
SetValue(NameProperty, value);
}
}
}

DependencyObject 和文章开篇的 ClassObject 中的 Name 有什么不同呢?

DependencyObject.Name 的实际值不是用字段保存在 DependencyObject 中,而是保存在 NameProperty 中,通过 SetValue 和 GetValue 来金星赋值取值操作。

在上述例子中,所有 DependncuObject 的对象将共用一个 NameProperty,这在现实中是不实际的:只要修改一个对象的属性,相当于所有对象的属性值都被修改了。所以,修改的属性,还是要维护在相应的对象中的:(修改部分用 ☆ 表示)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class DependencyProperty
{
private static int globalIndex = 0;// ☆
// 维护了一个全局的Map用来储存所有的DP
internal static Dictionary<object, DependencyProperty> RegisteredDps = new Dictionary<object, DependencyProperty>();
internal string Name;//注册属性名称
internal object Value;//属性值
internal int Index;// ☆
internal object HashCode;//Mape唯一键值

private DependencyProperty(string name, Type propertyName, Type ownerType, object defaultValue)
{
this.Name = name;
this.Value = defaultValue;
this.HashCode = name.GetHashCode() ^ ownerType.GetHashCode();
}
// 对外暴露一个Register方法用来注册新的DP
public static DependencyProperty Register(string name, Type propertyName, Type ownerType, object defaultValue)
{
DependencyProperty dp = new DependencyProperty(name, propertyName, ownerType, defaultValue);
globalIndex++;// ☆
dp.Index = globalIndex; // ☆
RegisteredDps.Add(dp.HashCode, dp);
return dp;
}
}

所有修改过的 DP 都保存在 EffectiveValueEntry 里,这样,就可以只保存修改的属性,未修改过的属性仍然读取 DP 的默认值,优化了属性的储存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class DependencyObject
{
// 引入有效的概念// ☆
private List<EffectiveValueEntry> _effectiveValues = new List<EffectiveValueEntry>();
// 注册一个新的DP:NameProperty
public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(DependencyObject), string.Empty);

public object GetValue(DependencyProperty dp)
{ // ☆
EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault(i => i.PropertyIndex == dp.Index);
if (effectiveValue.PropertyIndex != 0)
{
return effectiveValue.Value;
}
else
{
return DependencyProperty.RegisteredDps[dp.HashCode].Value;//仅此部分相同
}
}

public void SetValue(DependencyProperty dp, object value)
{ // 全部 ☆
EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault(i => i.PropertyIndex == dp.Index);
if (effectiveValue.PropertyIndex != 0)
{
effectiveValue.Value = value;
}
else
{
effectiveValue = new EffectiveValueEntry()
{
PropertyIndex = dp.Index,
Value = value
};
}
//DependencyProperty.RegisteredDps[dp.HashCode].Value = value;
}

public string Name
{
get
{
return (string)GetValue(NameProperty);
}
set
{
SetValue(NameProperty, value);
}
}
}
1
2
3
4
5
internal struct EffectiveValueEntry
{
internal int PropertyIndex{get;set;}
internal object Value{get;set;}
}

评论