当前位置:编程学习 > asp >>

使用反射+缓存+委托,实现一个不同对象之间同名同类型属性值的快速拷贝

最近实践一个DDD项目,在领域层与持久层之间,Domain Model与Entity Model之间有时候需要进行属性值得拷贝,而这些属性,尽管它所在的类名称不一样,但它们的属性名和属性类型差不多都是一样的。系统中有不少这样的Model需要相互转换,有朋友推荐使用AutoMapper,试了下果然不错,解决了问题,但作为一个老鸟,决定研究下实现原理,于是动手也来山寨一个。
为了让这个“轮子”尽量有实用价值,效率肯定是需要考虑的,所以决定采用“反射+缓存+委托”的路子。
 
第一次使用,肯定要反射出来对象的属性,这个简单,就下面的代码:
 
Type targetType;
//....
PropertyInfo[] targetProperties = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
这里只获取公开的实例对象的属性。
 
要实现同名同类型的属性拷贝,那么需要把这些属性找出来,下面是完整的代码:
 
 
 public ModuleCast(Type sourceType, Type targetType)
        {
            PropertyInfo[] targetProperties = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (PropertyInfo sp in sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                foreach (PropertyInfo tp in targetProperties)
                {
                    if (sp.Name == tp.Name && sp.PropertyType == tp.PropertyType)
                    {
                        CastProperty cp = new CastProperty();
                        cp.SourceProperty = new PropertyAccessorHandler(sp);
                        cp.TargetProperty = new PropertyAccessorHandler(tp);
                        mProperties.Add(cp);
                        break;
                    }
                }
            }
        }
 
这里使用了一个 CastProperty 类来保存要处理的源对象和目标对象,并且把这组对象放到一个CastProperty 列表的mProperties 静态对象里面缓存起来。
下面是 CastProperty 类的定义:
 
 
/// <summary>
        /// 转换属性对象
        /// </summary>
        public class CastProperty
        {
            public PropertyAccessorHandler SourceProperty
            {
                get;
                set;
            }
 
            public PropertyAccessorHandler TargetProperty
            {
                get;
                set;
            }
        }
 
类本身很简单,关键就是这个属性访问器PropertyAccessorHandler 对象,下面是它的定义:
 
 
 /// <summary>
        /// 属性访问器
        /// </summary>
        public class PropertyAccessorHandler
        {
            public PropertyAccessorHandler(PropertyInfo propInfo)
            {
                this.PropertyName = propInfo.Name;
                //var obj = Activator.CreateInstance(classType);
                //var getterType = typeof(FastPropertyAccessor.GetPropertyValue<>).MakeGenericType(propInfo.PropertyType);
                //var setterType = typeof(FastPropertyAccessor.SetPropertyValue<>).MakeGenericType(propInfo.PropertyType);
 
                //this.Getter = Delegate.CreateDelegate(getterType, null, propInfo.GetGetMethod());
                //this.Setter = Delegate.CreateDelegate(setterType, null, propInfo.GetSetMethod());
 
                if (propInfo.CanRead)
                    this.Getter = propInfo.GetValue;
 
                if (propInfo.CanWrite)
                    this.Setter = propInfo.SetValue;
            }
            public string PropertyName { get; set; }
            public Func<object, object[], object> Getter { get; private set; }
            public Action<object, object, object[]> Setter { get; private set; }
        }
 
在写这个类的时候,曾经走了好几次弯路,前期准备通过 Delegate.CreateDelegate 方式创建一个当前属性Get和Set方法的委托,但是经过数次测试发现,
Delegate.CreateDelegate(getterType, obj, propInfo.GetGetMethod());
 
这里的obj 要么是一个对象实例,要么是null,如果是null,那么这个委托定义只能绑定到类型的静态属性方法上;如果不是null,那么这个委托只能绑定到当前 obj 实例对象上,换句话说,如果将来用obj类型的另外一个实例对象,那么这个委托访问的还是之前那个obj 对象,跟新对象实例无关。
PS:为了走这条“弯路”,前几天还特意写了一个FastPropertyAccessor,申明了2个泛型委托,来绑定属性的Get和Set方法,即上面注释掉的2行代码:
 
 var getterType = typeof(FastPropertyAccessor.GetPropertyValue<>).MakeGenericType(propInfo.PropertyType);
 var setterType = typeof(FastPropertyAccessor.SetPropertyValue<>).MakeGenericType(propInfo.PropertyType);
好不容易将这个泛型委托创建出来了,编译也通过了,却发现最终没法使用,别提有多郁闷了:-《
 
回归话题,有了PropertyAccessorHandler,那么我们只需要遍历当前要转换的目标类型的属性集合,就可以开始对属性进行拷贝了:
 
 
 public void Cast(object source, object target)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (target == null)
             
补充:Web开发 , ASP.Net ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,