由于项目需要常常会遇到为某一个对象动态添加属性的情况,而以前我的实现方式是创建一个字典用于存放对象实例和它的值,但是往往光这么做是不够的,例如想在对象的某个属性值改变的时候做点什么都要写很多的代码,所以想是不是能够将这一类功能进行一下封装。后来因为学习WPF的缘故,想到依赖属性的思想和我需要的功能相近,但是又不能叫我把每一个想要添加扩展的对象类都去继承DependencyObject吧,而且有些类是封闭的不能够继承,所以依赖属性不能满足我的需求。不过说到底依赖属性还是个不错的东西,接下来我们将实现一个类似的东西 - 扩展属性。
在实现扩展属性时我也参考了依赖属性的源码,它的设计思想的确很“先进”。
1: private static ExtendProperty InfoProperty =
2: ExtendProperty.RegisterProperty("Info", typeof(string), typeof(UserInfo),"you win");
3: var user = new UserInfo() { Age=21, Name="maxzhang" };
4:
5: user.SetValue(InfoProperty, "hello");
6: string rrr = (string)user.GetValue(InfoProperty);
是不是看着特别像依赖属性呢,往下面看:
1: dynamic userDynamic = user.AsDynamic();
2: rrr= userDynamic.Info;
3: userDynamic.Info = "1";
4: userDynamic.Age = 50;
5: rrr = userDynamic.Info;
我为扩展属性添加了动态性使对象属性的创建和访问更加方便,这里如果Info属性在前面没有用RegisterProperty方法定义过它会自动生成一个扩展属性且添加属性值.如果访问了它的普通属性属性也是正常使用的。以上两个例子中
UserInfo类的定义是 public class UserInfo : ExtendObject { public string Name { set; get; } public int Age { set; get; }},你可能会问这不是和依赖属性一样吗?只是把继承DependencyObject换成了继承你自己写的ExtendObject 了。是的这样看是差不多的,不过以上的情况还是有一个好处的就是我可以在任何项目里引用它。
如果遇到了不能继承的情况呢,其实这种情况有很多。接…
public class UserInfo1 { public string Name{set;get;} } 这个类不继承任何类。
解决它这里引入了新的扩展类型AttachObject :
1: AttachObject user1Aobj = new AttachObject(user1);
2: var dyuser = user1Aobj.ToDynamicAttachObject();
3: //var dyuser = user1.ToDynamicAttachObject();
4: dyuser.Memo = "haha my name i's maxzhang......";
5: rrr = dyuser.Memo;
其实AttachObject 类型也是一个ExtendObject 可以把它看成是一个ExtendObject 的装饰。
与依赖属性类似,在ExtendProperty类中用了一个Dictionary<int,ExtendProperty>来存储系统中要用到的扩展属性,这样实现也达到了节省内存资源的目地。且这个类的构造器是一个private的,这样也就实现了一个单例模式,只有在RegisterProperty方法才能创造出一个ExtendProperty来.
1 public static ExtendProperty RegisterProperty(string propertyName, Type propertyType, Type ownerType,object defaultValue)
2 {
3 var property = new ExtendProperty(propertyName, propertyType,ownerType);
4 property.OverrideDefaultValue(ownerType, defaultValue);
5 ExtendPropertysProvider.Set(property.GetHashCode(), property);
6
7 return property;
8 }
用GetHashCode来标示我们这个属性的唯一性,这里我重写了这个函数它的值是this.ownerType.GetHashCode()^this.propertyName.GetHashCode(),也就是说用注册这个属性的类型和属性的名称确定了这个扩展属性。我们看到OverrideDefaultValue这个方法它是用来重写属性的默认值的,在这个系统中如果某个对象的扩展属性没有赋过值或说没有改变过,那么它应该在访问这个属性的时候取得一个默认值而且这个默认值应该是所有相同注册类型的对象共有的,而在用普通属性存储的对象中我们实例化对象后会在每一个对象中保存相应的默认值,这样无疑是浪费了内存。而且OverrideDefaultValue与AddOwner方法一起使用可以达到属性继承的目的。我们来看看AddOwner方法的实现:
1 public ExtendProperty AddOwner(Type ownerType,object defaultValue)
2 {
3 int newOwnerHash = ownerType.GetHashCode() ^ this.PropertyName.GetHashCode();
4 if(defaultValue!=null)
5 this.OverrideDefaultValue(ownerType, defaultValue);
6 ExtendPropertysProvider.Set(newOwnerHash, this);
7 return this;
8 }
使用AddOwner方法我们就在原有的扩展属性上添加了一个指向它的引用从而达到继承的目地,怎么重写属性默认值呢?其实很简单默认值在扩展属性中保存在一个<type,object>的字典中通过不同的类型我们就可以访问不同类型的相同属性的默认值了。
这里ExtendObject就没什么好说的了,原理就是其内部有一个Dictionary<int, object> propertyValues 存储着不同对象的值,用自身的GetHashCode ^ 扩展属性的HashCode 确定值的唯一性。
1 public class ExtendObject
2 {
3 protected Dictionary<int,object> propertyValues = new Dictionary<int,object>();
4 private Type OwnerType = null;
5
6 public ExtendObject()
7 {
8 OwnerType = this.GetType();
9 }
10
11 public override int GetHashCode()
12 {
13 return base.GetHashCode();
14 }
15
16 public virtual object GetOwner()
17 {
18 return this;
19 }
20
21 protected void AttachOwner(Type ownerType)
22 {
23 this.OwnerType = ownerType;
24 }
25
26 public bool IsExtendProperty(string propertyName)
27 {
28 return !OwnerType.GetProperties().Any(p => p.Name == propertyName); ;
29 }
30
31 protected ExtendProperty GetProperty(string name)
32 {
33 int propertyKey = OwnerType.GetHashCode() ^ name.GetHashCode();
34 var property = ExtendPropertysProvider.Get(propertyKey);
35 return property;
36 }
37
38 public object GetValue(ExtendProperty property)
39 {
40
41 int propertyHash = property.GetHashCode();
42 int key = this.GetHashCode() ^ propertyHash;
43
44 object result = null;
45 if (!propertyValues.TryGetValue(key, out result))
46 {
47 result = property.GetDefaultValue(this.OwnerType);
48 }
49 return result;
50 }
51
52 public bool ClearValue(ExtendProperty property)
53 {
54 bool result = false;
55 int propertyHash = property.GetHashCode();
56 int key = this.GetHashCode() ^ propertyHash;
57
58 if (propertyValues.Keys.Any(k => k == key))
59 {
60 propertyValues.Remove(key);
61 result = true;
62 }
63 return result;
64 }
65
66 public void SetValue(ExtendProperty property, object value)
67 {
68 var changedItemArgs = new ExtendPropertyValueChangedArgs();
69 int propertyHash = property.GetHashCode();
70 int key = this.GetHashCode() ^ propertyHash;
71
72 if (propertyValues.Keys.Any(k => k == key))
73 {
74 changedItemArgs.OldValue = propertyValues[key];
75 propertyValues[key] = value;
76 }
77 else
78 {
79 changedItemArgs.OldValue = null;
80 propertyValues.Add(key, value);
81 }
82
83 changedItemArgs.Item = GetOwner();
84 changedItemArgs.PropertyType = property.PropertyType;
85 changedItemArgs.PropertyName = property.PropertyName;
86 changedItemArgs.NewValue = value;
87
88 property.OnValueChanged(changedItemArgs);
89 }
90
91 public bool ClearValue(string propertyName)
92 {
93 var property = this.GetProperty(propertyName);
94 if (property != null)
95 return this.ClearValue(property);
96
97 return false;
98 }
99
100 public object GetValue(string propertyName)
101 {
102 var property = this.GetProperty(propertyName);
103 if (property != null)
104 return this.GetValue(property);
105
106 return null;
107 }
108
109 public void SetValue(string propertyName, object value)
110 {
111 var property = this.GetProperty(propertyName);
112
113 if (property != null)
114 {
115 this.SetValue(property, value);
116 }
117 else
118 {
119 var newProperty = ExtendProperty.RegisterProperty(propertyName, typeof(object), OwnerType);
120 this.SetValue(newProperty, value);
121 }
122 }
123
124 public ExtendDynamicObject AsDynamic()
125 {
126 return new ExtendDynamicObject(this);
127 }
128
129 }
不过这里还是有一个小小的技巧的就是OwnerType这个属性和AttachOwner方法,默认的OwnerType属性的值是扩展对象本身的Type,但是通过 AttachOwner方法我们可以改变这个属性从而达到将不继承自ExtendObject类型的对象装饰成ExtendObject对象的目地。
(3).也就是AttachObject
AttachObject类通过调用AttachOwner方法使用了这个技巧,同时把同样为ExtendObject的对象的属性统统都Copy过来
1 public class AttachObject : ExtendObject
2 {
3 private object owner;
4
5 public AttachObject(object obj)
6 : base()
7 {
8 owner = obj;
9 if (owner is ExtendObject)
10 {
11 Type ownerType = typeof(ExtendObject);
12 FieldInfo fInfo = ownerType.GetField("propertyValues", BindingFlags.Default | BindingFlags.NonPublic | BindingFlags.Instance);
13 var ownerValues = fInfo.GetValue(owner) as Dictionary;
14
15 foreach (var v in ownerValues)
16 this.propertyValues.Add(v.Key, v.Value);
17
18 }
19 this.AttachOwner(owner.GetType());
20 }
21
22 public override object GetOwner()
23 {
24 return owner;
25 }
26
27 public override int GetHashCode()
28 {
29 return owner.GetHashCode();
30 }
31 }
今天到这里
下一节中我将介绍如何实现动态性以及一些使用场景,代码下载……