zoukankan      html  css  js  c++  java
  • c# 表达式目录树拷贝对象(根据对象类型动态生成表达式目录树)

    表达式目录树,在C#中用Expression标识,这里就不介绍表达式目录树是什么了,有兴趣可以自行百度搜索,网上资料还是很多的。

    这里主要分享的是如何动态构建表达式目录树。

    构建表达式目录树的代码挺简单的,但是感觉不容易记住,我这边主要是根据反编译工具ILSpy来查看自己写已经写好的一个表达式(反编译里面的代码就是拆解表达式后再拼装的,下面会给例子),然后再稍微改动一下,这样有助于理解如何去手动拼装一个表达式。

    既然是拷贝对象,先来一个最简单的对象拷贝的表达式:

    假设有类:

    public class Cat
        {
            public string Name { get; set; }
            public string Colour { get; set; }
        }
    
        public class Dog
        {
            public string Name { get; set; }
            public string Colour { get; set; }
        }

    有一天,我发现邻居的猫和我家的狗毛色长得一样,邻居的狗名字叫的很好听,颜色也很好看,于是,我也想给我家的猫取相同的名字,把毛色也染成相同的颜色:

    Dog dog = new Dog { Name = "二哈", Colour  = "黄色" };
    Cat cat = new Cat { Name = dog.Name, Colour = dog.Colour};

    上面两句代码,转换成表达式目录树:

    //定义一个表达式
    Expression<Func<Dog, Cat>> exp = d => new Cat { Name = d.Name, Colour = d.Colour };

    把我家的猫也取相同名字,也染毛色:

    Cat c = exp.Compile().Invoke(dog);

    借助ILSpy反编译工具,查看表达式Expression<Func<Dog, Cat>> exp = d => new Cat { Name = d.Name, colour = d.Colour };反编译生产的代码

    ParameterExpression parameterExpression = Expression.Parameter(typeof(Dog), "d");
        Expression<Func<Dog, Cat>> expression = Expression.Lambda<Func<Dog, Cat>>(Expression.MemberInit(Expression.New(typeof(Cat)), new MemberBinding[]
        {
            Expression.Bind((MethodInfo)MethodBase.GetMethodFromHandle(ldtoken(set_Name())), Expression.Property(parameterExpression, (MethodInfo)MethodBase.GetMethodFromHandle(ldtoken(get_Name())))), 
            Expression.Bind((MethodInfo)MethodBase.GetMethodFromHandle(ldtoken(set_Colour())), Expression.Property(parameterExpression, (MethodInfo)MethodBase.GetMethodFromHandle(ldtoken(get_Colour()))))
        }), new ParameterExpression[]
        {
            parameterExpression
        });

    分析此代码,(MethodInfo)MethodBase.GetMethodFromHandle(ldtoken(set_Name())) 类似这种代码我们编译器是不认识的,替换成反射获取Set_Name方法,整理之后代码如下:

     1 ParameterExpression parameterExpression = Expression.Parameter(typeof(Dog), "d"); //定义一个变量
     2                 
     3 Type typeDog = typeof(Dog);
     4 MethodInfo getName = typeDog.GetMethod("get_Name"); //获取get_Name()方法
     5 MethodInfo getColour = typeDog.GetMethod("get_Colour"); //获取get_Colour()方法
     6 Type typeCat = typeof(Cat);
     7 MethodInfo setName = typeCat.GetMethod("set_Name"); //获取set_Name()方法
     8 MethodInfo setColour = typeCat.GetMethod("set_Colour"); //获取set_Colour()方法
     9 
    10 
    11 MemberExpression dName = Expression.Property(parameterExpression, getName);//d.Name
    12 MemberAssignment name_dName = Expression.Bind(setName, dName);//Name = d.Name
    13 MemberExpression dColour = Expression.Property(parameterExpression, getColour);//d.Colour
    14 MemberAssignment colour_dColour = Expression.Bind(setColour, dColour);//Colour = d.Colour
    15 
    16 //new Cat { Name = d.Name, Colour = d.Colour }; 表达式主体完成
    17 MemberInitExpression body = Expression.MemberInit(Expression.New(typeof(Cat)),
    18    new MemberBinding[]
    19    {
    20        name_dName,
    21        colour_dColour
    22    });
    23 //转换为表达式
    24 Expression<Func<Dog, Cat>> expression = Expression.Lambda<Func<Dog, Cat>>(body
    25 , new ParameterExpression[]
    26   {
    27       parameterExpression
    28   }
    29 );
    30 
    31  Cat c2 = expression.Compile().Invoke(dog);//编译表达式树由描述为可执行代码的 lambda 表达式,并生成一个委托执行

    到这里,很多人会有疑问,你都自己写了表达式目录树了,还手动拼装一个一模一样的?而且手动拼装这么麻烦?有什么作用?往下看例子

    有天去动物园,看见一只老虎,于是我又想到邻居的狗和我家的猫...于是乎我又想把自家的猫当做老虎了...于是,又写了下面这条表达式

    Expression<Func<Tiger, Cat>> exp = d => new Cat { Name = d.Name, Colour = d.Colour };

    但是这样子不对啊?难道我每次换个类型,都要写一条表达式吗?这样明显是不合适的,所以我们需要封装一个方法,动态拼装出两个对象属性赋值的完整表达式,这个封装就不详细写了,上面已经写了如何去拼装一个表达式了,虽然只有部分,更多的可以自己写个复杂的表达式查看反编译代码。

    这里用到泛型类作为缓存,否则此种写法没意义,还不如直接用反射来写。

    单层拷贝:

     1 /// <summary>
     2     /// 动态生成表达式目录树方式 浅拷贝 对象
     3     /// 采用泛型类作为静态缓存,即<TIn,TOut>同一类型只会生成一次表达式目录树。(如果不缓存,和直接存反射没啥区别,不如直接用纯反射的方式深拷贝)
     4     /// </summary>
     5     /// <typeparam name="TIn"></typeparam>
     6     /// <typeparam name="TOut"></typeparam>
     7     public class ExpressionCloneObject<TIn,TOut>
     8     {
     9         private static Func<TIn, TOut> _FUNC = null;
    10 
    11         static ExpressionCloneObject()
    12         {
    13             _FUNC = _CloneFunc();
    14         }
    15 
    16         public static TOut Invoke(TIn t)
    17         {
    18             return _FUNC.Invoke(t);
    19         }
    20 
    21         private static Func<TIn, TOut> _CloneFunc()
    22         {
    23             Type typeT1 = typeof(TIn);
    24             Type typeT2 = typeof(TOut);
    25             ParameterExpression parameterExpression = Expression.Parameter(typeT1, "t");
    26             List<MemberBinding> memberList = new List<MemberBinding>();
    27             //属性
    28             foreach (PropertyInfo propertie in typeT2.GetProperties())
    29             {
    30                 PropertyInfo propertieT1 = typeT1.GetProperty(propertie.Name);
    31                 if (propertieT1 != null)
    32                 {
    33                     MethodInfo mSet = typeT2.GetMethod("set_" + propertie.Name, new Type[] { propertie.PropertyType });
    34                     memberList.Add(Expression.Bind(mSet, Expression.Property(parameterExpression, propertieT1)));
    35                 }
    36                 else
    37                 {
    38                     FieldInfo fieldT1 = typeT1.GetField(propertie.Name);//可能T2中的属性对应T1中的字段
    39                     if (fieldT1 != null)
    40                     {
    41                         MethodInfo mSet = typeT2.GetMethod("set_" + propertie.Name, new Type[] { propertie.PropertyType });
    42                         memberList.Add(Expression.Bind(mSet, Expression.Field(parameterExpression, fieldT1)));
    43                     }
    44                 }
    45             }
    46             //字段
    47             foreach (FieldInfo field in typeT2.GetFields())
    48             {
    49                 FieldInfo fieldT1 = typeT1.GetField(field.Name);
    50                 if (fieldT1 != null)
    51                 {
    52                     memberList.Add(Expression.Bind(field, Expression.Field(parameterExpression, fieldT1)));
    53                 }
    54                 else
    55                 {
    56                     PropertyInfo propertieT1 = typeT1.GetProperty(field.Name);
    57                     if (propertieT1 != null)//可能T2中的字段对应T1中的属性
    58                     {
    59                         memberList.Add(Expression.Bind(field, Expression.Property(parameterExpression, propertieT1)));
    60                     }
    61                 }
    62             }
    63             MemberInitExpression body = Expression.MemberInit(
    64                             Expression.New(typeT2)
    65                             , memberList.ToArray()
    66                         );
    67             Expression<Func<TIn, TOut>> expression = Expression.Lambda<Func<TIn, TOut>>(body, new ParameterExpression[] { parameterExpression });
    68 
    69             return expression.Compile();
    70         }
    71     }
    View Code

    深层拷贝:

      1 /// <summary>
      2     /// 
      3     /// 动态生成表达式目录树方式 深拷贝 对象
      4     /// 采用泛型类作为静态缓存,即<TIn,TOut>同一类型只会生成一次表达式目录树。(如果不缓存,和直接存反射没啥区别,不如直接用纯反射的方式拷贝)
      5     /// (注:对象的属性 不支持数组或集合,原因是对象中的数组或集合的个数可能不一样
      6     /// ,并且这里用泛型缓存,所以以第一次的数组或集合生成的表达式目录树会缓存起来,下次直接拷贝对象如果集合中的个数和缓存中的不一致
      7     /// 会导致拷贝结果不正确。解决方法可以先拷贝属性中的集合,再赋值回到此方法拷贝出来的对象上。或者换种深拷贝方法,深拷贝方式很多,如
      8     /// 序列化、存反射、AutoMapper等等)
      9     /// </summary>
     10     /// <typeparam name="TIn"></typeparam>
     11     /// <typeparam name="TOut"></typeparam>
     12     public class ExpressionDeepCloneObject<TIn,TOut> where TOut : class, new()
     13     {
     14         private static Func<TIn, TOut> _FUNC = null;
     15 
     16         public static TOut Invoke(TIn t)
     17         {
     18             if(_FUNC == null)
     19             {
     20                 _FUNC = _DeepClone(t);
     21             }
     22             return _FUNC.Invoke(t);
     23         }
     24 
     25         /// <summary>
     26         /// 表达式目录树深拷贝
     27         /// </summary>
     28         /// <typeparam name="TIn">被拷贝对象类型</typeparam>
     29         /// <typeparam name="TOut">新对象类型</typeparam>
     30         /// <param name="tIn">被拷贝对象</param>
     31         /// <returns>表达式目录树Lambda</returns>
     32         private static Func<TIn, TOut> _DeepClone(TIn tIn)  
     33         {
     34             TOut tOut = new TOut();
     35             ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "t");
     36             Expression<Func<TIn, TOut>> expression = Expression.Lambda<Func<TIn, TOut>>(_DeepClone(tIn, tOut, parameterExpression), new ParameterExpression[] { parameterExpression });
     37             return expression.Compile();
     38         }
     39 
     40         private static MemberInitExpression _DeepClone(object t1, object t2, Expression parameterExpression)
     41         {
     42             Type typeT1 = t1.GetType();
     43             Type typeT2 = t2.GetType();
     44             List<MemberBinding> memberList = new List<MemberBinding>();
     45             //属性部分
     46             foreach (PropertyInfo propertie in typeT2.GetProperties())
     47             {
     48                 PropertyInfo propertieT1 = typeT1.GetProperty(propertie.Name);
     49                 if (propertieT1 != null && propertieT1.GetValue(t1) != null)
     50                 {
     51                     MethodInfo mSet = typeT2.GetMethod("set_" + propertie.Name, new Type[] { propertie.PropertyType });
     52                     if (!propertieT1.GetValue(t1).GetType().IsValueType && propertieT1.GetValue(t1).GetType().Name != "String")//引用类型(不包括string)
     53                     {
     54                         memberList.Add(Expression.Bind(mSet, _DeepClone(propertieT1.GetValue(t1), propertieT1.GetValue(t1), Expression.Property(parameterExpression, propertieT1))));
     55                     }
     56                     else
     57                     {
     58                         memberList.Add(Expression.Bind(mSet, Expression.Property(parameterExpression, propertieT1)));
     59                     }
     60                 }
     61                 else
     62                 {
     63                     FieldInfo fieldT1 = typeT1.GetField(propertie.Name);//可能t2中的属性对应t1中的字段
     64                     if (fieldT1 != null && fieldT1.GetValue(t1) != null)
     65                     {
     66                         MethodInfo mSet = typeT2.GetMethod("set_" + propertie.Name, new Type[] { propertie.PropertyType });
     67                         if (!fieldT1.GetValue(t1).GetType().IsValueType && fieldT1.GetValue(t1).GetType().Name != "String")//引用类型(不包括string)
     68                         {
     69                             memberList.Add(Expression.Bind(mSet, _DeepClone(fieldT1.GetValue(t1), fieldT1.GetValue(t1), Expression.Field(parameterExpression, fieldT1))));
     70                         }
     71                         else
     72                         {
     73                             memberList.Add(Expression.Bind(mSet, Expression.Field(parameterExpression, fieldT1)));
     74                         }
     75                     }
     76                 }
     77             }
     78             //字段部分
     79             foreach (FieldInfo field in typeT2.GetFields())
     80             {
     81                 FieldInfo fieldT1 = typeT1.GetField(field.Name);
     82                 if (fieldT1 != null && fieldT1.GetValue(t1) != null)
     83                 {
     84                     if (!fieldT1.GetValue(t1).GetType().IsValueType && fieldT1.GetValue(t1).GetType().Name != "String")//引用类型(不包括string)
     85                     {
     86                         memberList.Add(Expression.Bind(field, _DeepClone(fieldT1.GetValue(t1), fieldT1.GetValue(t1), Expression.Field(parameterExpression, fieldT1))));
     87                     }
     88                     else
     89                     {
     90                         memberList.Add(Expression.Bind(field, Expression.Field(parameterExpression, fieldT1)));
     91                     }
     92                 }
     93                 else
     94                 {
     95                     PropertyInfo propertieT1 = typeT1.GetProperty(field.Name);
     96                     if (propertieT1 != null && propertieT1.GetValue(t1) != null)//可能t2中的字段对应t1中的属性
     97                     {
     98                         if (!propertieT1.GetValue(t1).GetType().IsValueType && propertieT1.GetValue(t1).GetType().Name != "String") //引用类型(不包括string)
     99                         {
    100                             memberList.Add(Expression.Bind(field, _DeepClone(propertieT1.GetValue(t1), propertieT1.GetValue(t1), Expression.Property(parameterExpression, propertieT1))));
    101                         }
    102                         else
    103                         {
    104                             memberList.Add(Expression.Bind(field, Expression.Property(parameterExpression, propertieT1)));
    105                         }
    106                     }
    107                 }
    108             }
    109             MemberInitExpression body = Expression.MemberInit(
    110                             Expression.New(typeT2)
    111                             , memberList.ToArray()
    112                         );
    113             return body;
    114         }
    115     }
    View Code

    当有这个封装之后,只需要如下写,会自动拼装出想要的表达式返回想要的结果

    Cat c3 = ExpressionCloneObject<Tiger, Cat>.Invoke(new Tiger { Name = "Tiger", Colour = "黄色" });
  • 相关阅读:
    Microsoft .NET Framework 以及 CLR 的版本
    如何:备份 Team Foundation Server
    通过VS2008SP1 访问TFS2010
    Project 2007 Understanding Project's Percent Complete vs. Percent Work Complete
    TFS:从单服务器部署移到双服务器部署
    C#网络编程(基本概念和操作) Part.1
    图解Windows server 2012故障转移群集的安装、建立
    产品经理经验总结
    TCP同步与异步及阻塞模式,多线程+阻塞模式,非阻塞模式简单介绍
    Windows Phone开发概论
  • 原文地址:https://www.cnblogs.com/fnz0/p/11468723.html
Copyright © 2011-2022 走看看