动手实现扩展属性为对象动态添加获取数据

由于项目需要常常会遇到为某一个对象动态添加属性的情况,而以前我的实现方式是创建一个字典用于存放对象实例和它的值,但是往往光这么做是不够的,例如想在对象的某个属性值改变的时候做点什么都要写很多的代码,所以想是不是能够将这一类功能进行一下封装。后来因为学习WPF的缘故,想到依赖属性的思想和我需要的功能相近,但是又不能叫我把每一个想要添加扩展的对象类都去继承DependencyObject吧,而且有些类是封闭的不能够继承,所以依赖属性不能满足我的需求。不过说到底依赖属性还是个不错的东西,接下来我们将实现一个类似的东西 - 扩展属性。

在实现扩展属性时我也参考了依赖属性的源码,它的设计思想的确很“先进”。

1.先来看看扩展属性的使用方式:

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 的装饰。

2.下面我们来看看这些都是怎么实现的

(1).ExtendProperty

         与依赖属性类似,在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>的字典中通过不同的类型我们就可以访问不同类型的相同属性的默认值了。

(2).ExtendObject

 这里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 =&gt; 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 =&gt; 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 =&gt; 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 }

今天到这里

下一节中我将介绍如何实现动态性以及一些使用场景,代码下载……

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

EnterLib PIAB又一个BUG?[续]——这是一个致命的BUG

在《EnterLib PIAB又一个BUG?》这篇文章中我们谈到:当我们通过应用DependencyAttribute特性定义需要自动注入的属性的时候,当这个属...

241100
来自专栏领域驱动设计DDD实战进阶

05-TypeScript中的方法新功能(下)

再TypeScript中,方法还有一些新功能能够让我们更好的控制方法执行。 1.Generator方法: yield关键字用于控制方法在执行的时候暂停住,后续方...

28250
来自专栏封碎

Android获取应用程序的大小 博客分类: Android AndroidOSF#Security

       今天碰到个问题,想获取某个已安装的包的大小,没找到合适的方法。搜索了一下,发现PackageManager里面有个getPackageSizeIn...

16120
来自专栏大内老A

采用一个自创的"验证框架"实现对数据实体的验证[扩展篇]

关于“验证框架”,先后推出了《编程篇》、《设计篇》和《改进篇》,本不打算再写《XXX篇》的。但是今天收到两个园友的短消息,想了解一下如何定义自己的验证规则。这实...

22570
来自专栏技术博客

编写高质量代码改善C#程序的157个建议[泛型集合、选择集合、集合的安全]

    软件开发过程中,不可避免会用到集合,C#中的集合表现为数组和若干集合类。不管是数组还是集合类,它们都有各自的优缺点。如何使用好集合是我们在开发过程中必须...

8320
来自专栏大内老A

深入探讨ASP.NET MVC的筛选器

在ActionInvoker对Action的执行过程中,除了通过利用ActionDescriptor对Action方法的执行,以及之前进行的Model绑定与验证...

21280
来自专栏Python小屋

Python版归并排序算法(附Python程序__name__属性用法演示视频)

import random def mergeSort(seq, reverse=False): #把原列表分成两部分 mid = len(s...

35060
来自专栏大内老A

ASP.NET MVC是如何运行的(4): Action的执行

作为Controller基类ControllerBase的Execute方法的核心在于对Action方法的执行和作为方法返回的ActionResult的执行,两...

20080
来自专栏Linyb极客之路

工作流引擎之activiti委托功能实现

异常的意思就是完成任务是要先解决委托,委托任务必须有解决委托这一步骤,当解决委托后,流程并不是进行到下一个节点,而是需要被委托人有完成任务操作时方可进行到下一步...

49330
来自专栏逸鹏说道

【推荐】C#线程篇---Task(任务)和线程池不得不说的秘密(5.1)

在上篇最后一个例子之后,我们发现了怎么去使用线程池,调用ThreadPool的QueueUserWorkItem方法来发起一次异步的、计算限制的操作,例子很简单...

46250

扫码关注云+社区

领取腾讯云代金券