随着.net引入attribute,实体类在编程中的重要性已经越来越重要了,并且有越来越多的工具开始生成自己的实体类。但是这也引来了一个麻烦,每个工具生成的实体类需要各自的attribute,并且可能互相不通用。

    例如:Entity Framework生成的实体类有:Serializable,DataContract,EdmEntityType等,属性有DataMember,EdmScalarProperty等,entity framework所需要的各种特性,并且附带了wcf、xml序列化所需要的特性。但是,当你需要把这些实体做一个特殊的操作,并且这个操作又需要一些特殊的attribute时,至少有下面两种选择:

      1、直接修改生成的实体类,但是这样做了以后,通常会因为重新生成一边实体类而丢失了所有已经标记的attribute,这几乎让人无法接受。

      2、再定义一套实体类,但是,这样做的缺点是你需要手动的copy每一个值,这个工作有让人感到麻烦而无奈。

    所以,我们不得不寻找其他方案:

      1、找一个可以自己控制的代码生成器(例如:codesmith),但是,总有那些用现成工具很难配置的代码生成。

      2、运用反射,根据某种契约(例如:属性名称相同)自动复制数据,但是,传统反射的效率是在是一个大问题。

    但是,别忘了反射可以直接Emit出IL,这样可以极大提高复制速度,达到接近直接代码手写手动代码copy的效率,本文就是提供了这么一种简单的实现。

 

    第一步,就是确定核心功能Copy方法的签名,

    刚开始定义成了:

void Copy<T, TResult>(T value, TResult result);
    但是,不久就发现这个定义在很多场合不适合,例如TResult为值类型时,那么对TResult的任何复制操作将都是白搭。

    又经过了几次修订以后,最终确定为:

void Copy<T, TResult>(T value, ref TResult result);
    这个方法签名至少可以应对各种值类型和应用类型的情况。

    第二步,就是确定该封装些什么,怎么允许扩展,顺便取个名字

    为了保证功能和扩展性,决定用装饰模式,大概为:

Code

    当然,还要会基类中增加Cache服务,把Emit出来的方法cache下来,提高复制效率。当然,先要定义Cache的值(当然和Copy方法的签名一致)

    public delegate void CopyDelegate<TSource, TResult>(TSource source, ref TResult result);
    以及Cache的键
Code

    然后,拥有Cache服务的基类将变成:

Code

    哦,对了,在这里,无论TSource还是TResult必须要是公开类型,否则的话,会遇上权限问题,所以,在基类里面加一个方法:

        protected static bool CheckType(Type type)
        {
            
return type.IsVisible;
        }

    这样,我们就可以实现自动复制服务了:

Code

    来看一个简单的应用:

Code

    看起来不错,基本的属性对属性的复制已经没问题了,而且,还可以支持Nested,或者值类型到引用类型,或者引用类型到值类型的强大复制功能,例如:

Code

    但是,这个简单的复制不是万能的,必须要能够被扩展,前面说过要使用装饰模式来扩展性的功能,那么,先定义个装饰的基类:

Code

    是不是很简单,如何扩展实现哪?这里就以一个简单的自定义复制器为例:

Code

    来看看怎么用这个自定义复制器:

Code

    是不是很简单,当然,还可以写其他很多装饰,例如:数组复制的装饰,枚举复制的装饰,大家可以发挥自己的想象力。

posted on 2009-04-27 15:18  Zhenway  阅读(4260)  评论(5编辑  收藏  举报