第十五节:Expression表达式目录树(与委托的区别、自行拼接、总结几类实例间的拷贝)

一. 基本介绍

回忆: 最早接触到表达式目录树(Expression)可能要追溯到几年前使用EF早期的时候,发现where方法里的参数是Expression<Func<T,bool>>这么一个类型,当初不是很理解,只是知道传入lambda表达式使用即可,对于Expression和里面的Func<T,bool>到底是怎么一种关系,都不清楚。

  今天,带着回忆开发初期的心情,详细的介绍一下这一段时间对Expression的理解。

1. Expression与Func委托的区别

  ①:委托是一种类型,是方法的抽象,通过委托可以将方法以参数的形式传递给另一个方法,同时调用委托的时候,它所包含的方法都会被实现。委托的关键字是delegate,可以自定义委托,也可以使用内置委托,通过简化,可以将Lambda表达式或Lambda语句赋值给委托,委托的调用包括同步调用和异步调用。

  ②:表达式目录树(Expression),是一种数据结构,可以利用Lambda表达式进行声明,Lambda表达式的规则要符合Expression中Func委托的参数规则。

可以利用Lambda表达式进行声明,但Lambda语句是不能声明的。

  Expression调用Compile方法可以转换成<TDelegate>中的委托。

回顾委托的代码:

 1  {
 2                 //1. Func委托,必须要有返回值,最后一个参数为返回值,前面为输入参数
 3                 Func<int, int, int> func1 = new Func<int, int, int>((int m, int n) =>
 4                 {
 5                     return m * n + 2;
 6                 });
 7                 //对其进行最简化(Lambda语句)
 8                 Func<int, int, int> func2 = (m, n) =>
 9                 {
10                     return m * n + 2;
11                 };
12                 //对其进行最简化(Lambda表达式)
13                 Func<int, int, int> func3 = (m, n) => m * n + 2;
14                 //调用委托
15                 int result1 = func1.Invoke(2, 3);
16                 int result2 = func2.Invoke(2, 3);
17                 int result3 = func3.Invoke(2, 3);
18                 Console.WriteLine("委托三种形式结果分别为:{0},{1},{2}", result1, result2, result3);
19 }

初识Expression表达式目录树的代码

 1  {
 2                 //报错 (Lambda语句无法转换成表达式目录树)
 3                 //Expression<Func<int, int, int>> exp1 = (m, n) =>
 4                 //{
 5                 //    return m * n + 2;
 6                 //};
 7 
 8                 //利用Lambda表达式  来声明 表达式目录树
 9                 Expression<Func<int, int, int>> exp2 = (m, n) => m * n + 2;
10 
11                 //利用Compile编译,可以将表达式目录树转换成委托
12                 Func<int, int, int> func = exp2.Compile();
13                 int result1 = func.Invoke(2, 3);
14                 Console.WriteLine("表达式目录树转换成委托后结果为:{0}", result1);
15 }

执行结果

2. 自己拼接表达式目录树

 ①. 核心:先把Lambda表达式写出来,然后用ILSpy工具进行编译,就能把表达式目录树的拼接过程给显示出来,当然有些代码不是我们想要的,需要转换成C#代码。

 ②. 了解拼接要用到的类型和方法:

 类型:常量值表达式(ConstantExpression)、命名参数表达式(ParameterExpression)、含二元运算符的表达式(BinaryExpression)

 方法:设置常量表达式的值(Constant方法)、设置参数表达式的变量(Parameter方法)、相加(Add方法)、相乘(Multiply方法)、Lambda方法:将拼接的二元表达式转换成表达式目录树

 ③. 步骤:声明变量和常量→进行二元计算→将最终二元运算符的表达式转换成表达式目录树

 下面分享拼接 Expression<Func<int, int, int>> express1 = (m, n) => m * n + 2; 的代码:

{
                Func<int, int, int> func1 = (m, n) => m * n + 2;
                //利用反编译工具翻译下面这个Expression
                Expression<Func<int, int, int>> express1 = (m, n) => m * n + 2;
                //下面是反编译以后的代码(自己稍加改进了一下)
                ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "m");
                ParameterExpression parameterExpression2 = Expression.Parameter(typeof(int), "n");
                BinaryExpression binaryExpression = Expression.Multiply(parameterExpression, parameterExpression2);
                ConstantExpression constantExpression = Expression.Constant(2, typeof(int));
                BinaryExpression binaryFinalBody = Expression.Add(binaryExpression, constantExpression);
                Expression<Func<int, int, int>> express = Expression.Lambda<Func<int, int, int>>(binaryFinalBody, new ParameterExpression[]
                {
                    parameterExpression, 
                    parameterExpression2
                });
                int result = express.Compile().Invoke(2, 3);
                Console.WriteLine("自己拼接的表达式目录树的结果为:{0}", result);
 } 

运行结果:

 

二. 实体间Copy赋值的几类处理方案

背景: 在实际开发中,我们可能经常会遇到这种场景,两个实体的名称不同,属性完全相同,需要将一个实体的值赋值给另一个对应实体上的属性。

解决这类问题通常有以下几种方案:

  1. 直接硬编码的形式:速度最快(0.126s)
  2. 通过反射遍历属性的形式 (6.328s)
  3. 利用序列化和反序列化的形式:将复制实体序列化字符串,在把该字符串反序列化被赋值实体(7.768s)
  4. 字典缓存+表达式目录树(Lambda的拼接代码了解即可) (0.663s)
  5. 泛型缓存+表达式目录树(Lambda的拼接代码了解即可) (2.134s)

 代码分享:

  1  public static class CopyUtils
  2     {
  3         //字典缓存
  4         private static Dictionary<string, object> _Dic = new Dictionary<string, object>();
  5 
  6         public static void Show()
  7         {
  8             //0. 准备实体 
  9             User user = new User()
 10             {
 11                 id = 1,
 12                 userName = "ypf",
 13                 userAge = 3
 14             };
 15             long time1 = 0;
 16             long time2 = 0;
 17             long time3 = 0;
 18             long time4 = 0;
 19             long time5 = 0;
 20 
 21             #region 1-直接硬编码的形式
 22             {
 23                 Task.Run(() =>
 24                 {
 25                     Stopwatch watch = new Stopwatch();
 26                     watch.Start();
 27                     for (int i = 0; i < 1000000; i++)
 28                     {
 29                         UserCopy userCopy = new UserCopy()
 30                         {
 31                             id = user.id,
 32                             userName = user.userName,
 33                             userAge = user.userAge,
 34                         };
 35                     }
 36                     watch.Stop();
 37                     time1 = watch.ElapsedMilliseconds;
 38                     Console.WriteLine("方案1所需要的时间为:{0}", time1);
 39                 });
 40 
 41             }
 42             #endregion
 43 
 44             #region 2-反射遍历属性
 45             {
 46                 Task.Run(() =>
 47                 {
 48                     Stopwatch watch = new Stopwatch();
 49                     watch.Start();
 50                     for (int i = 0; i < 1000000; i++)
 51                     {
 52                         CopyUtils.ReflectionMapper<User, UserCopy>(user);
 53                     }
 54                     watch.Stop();
 55                     time2 = watch.ElapsedMilliseconds;
 56                     Console.WriteLine("方案2所需要的时间为:{0}", time2);
 57                 });
 58             }
 59             #endregion
 60 
 61             #region 3-序列化和反序列化
 62             {
 63                 Task.Run(() =>
 64                 {
 65                     Stopwatch watch = new Stopwatch();
 66                     watch.Start();
 67                     for (int i = 0; i < 1000000; i++)
 68                     {
 69                         CopyUtils.SerialzerMapper<User, UserCopy>(user);
 70                     }
 71                     watch.Stop();
 72                     time3 = watch.ElapsedMilliseconds;
 73                     Console.WriteLine("方案3所需要的时间为:{0}", time3);
 74                 });
 75                 
 76             }
 77             #endregion
 78 
 79             #region 04-字典缓存+表达式目录树
 80             {
 81                 Task.Run(() =>
 82                 {
 83                     Stopwatch watch = new Stopwatch();
 84                     watch.Start();
 85                     for (int i = 0; i < 1000000; i++)
 86                     {
 87                         CopyUtils.DicExpressionMapper<User, UserCopy>(user);
 88                     }
 89                     watch.Stop();
 90                     time4 = watch.ElapsedMilliseconds;
 91                     Console.WriteLine("方案4所需要的时间为:{0}", time4);
 92                 });
 93             }
 94             #endregion
 95 
 96             #region 05-泛型缓存+表达式目录树
 97             {
 98                 Task.Run(() =>
 99                 {
100                     Stopwatch watch = new Stopwatch();
101                     watch.Start();
102                     for (int i = 0; i < 1000000; i++)
103                     {
104                         GenericExpressionMapper<User, UserCopy>.Trans(user);
105                     }
106                     watch.Stop();
107                     time5 = watch.ElapsedMilliseconds;
108                     Console.WriteLine("方案5所需要的时间为:{0}", time5);
109                 });
110             }
111             #endregion
112      
113         }

上述代码涉及到的几个封装

 1  #region 封装-反射的方式进行实体间的赋值
 2         /// <summary>
 3         /// 反射的方式进行实体间的赋值
 4         /// </summary>
 5         /// <typeparam name="TIn">赋值的实体类型</typeparam>
 6         /// <typeparam name="TOut">被赋值的实体类型</typeparam>
 7         /// <param name="tIn"></param>
 8         public static TOut ReflectionMapper<TIn, TOut>(TIn tIn)
 9         {
10             TOut tOut = Activator.CreateInstance<TOut>();
11             //外层遍历获取【被赋值的实体类型】的属性
12             foreach (var itemOut in tOut.GetType().GetProperties())
13             {
14                 //内层遍历获取【赋值的实体类型】的属性
15                 foreach (var itemIn in tIn.GetType().GetProperties())
16                 {
17                     if (itemOut.Name.Equals(itemIn.Name))
18                     {
19                         //特别注意这里:SetValue和GetValue的用法
20                         itemOut.SetValue(tOut, itemIn.GetValue(tIn));
21                         break;
22                     }
23                 }
24             }
25             return tOut;
26         }
27         #endregion
28 
29         #region 封装-序列化反序列化进行实体见的赋值
30         /// <summary>
31         /// 序列化反序列化进行实体见的赋值
32         /// </summary>
33         /// <typeparam name="TIn">赋值的实体类型</typeparam>
34         /// <typeparam name="TOut">被赋值的实体类型</typeparam>
35         /// <param name="tIn"></param>
36         public static TOut SerialzerMapper<TIn, TOut>(TIn tIn)
37         {
38             return JsonConvert.DeserializeObject<TOut>(JsonConvert.SerializeObject(tIn));
39         }
40         #endregion
41 
42         #region 封装-字典缓存+表达式目录树
43         /// <summary>
44         /// 序列化反序列化进行实体见的赋值
45         /// </summary>
46         /// <typeparam name="TIn">赋值的实体类型</typeparam>
47         /// <typeparam name="TOut">被赋值的实体类型</typeparam>
48         /// <param name="tIn"></param>
49         public static TOut DicExpressionMapper<TIn, TOut>(TIn tIn)
50         {
51             string key = string.Format("funckey_{0}_{1}", typeof(TIn).FullName, typeof(TOut).FullName);
52             if (!_Dic.ContainsKey(key))
53             {
54                 ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
55                 List<MemberBinding> memberBindingList = new List<MemberBinding>();
56                 foreach (var item in typeof(TOut).GetProperties())
57                 {
58                     MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
59                     MemberBinding memberBinding = Expression.Bind(item, property);
60                     memberBindingList.Add(memberBinding);
61                 }          
62                 MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
63                 Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
64                 {
65                     parameterExpression
66                 });
67                 Func<TIn, TOut> func = lambda.Compile();//拼装是一次性的
68                 _Dic[key] = func;
69             }
70             return ((Func<TIn, TOut>)_Dic[key]).Invoke(tIn);
71         }
72         #endregion
View Code
 1 /// <summary>
 2     /// 泛型缓存
 3     /// </summary>
 4     /// <typeparam name="TIn"></typeparam>
 5     /// <typeparam name="TOut"></typeparam>
 6     public class GenericExpressionMapper<TIn, TOut>
 7     {
 8         private static Func<TIn, TOut> _FUNC = null;
 9         static GenericExpressionMapper()
10         {
11             ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
12             List<MemberBinding> memberBindingList = new List<MemberBinding>();
13             foreach (var item in typeof(TOut).GetProperties())
14             {
15                 MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
16                 MemberBinding memberBinding = Expression.Bind(item, property);
17                 memberBindingList.Add(memberBinding);
18             }
19             foreach (var item in typeof(TOut).GetFields())
20             {
21                 MemberExpression property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));
22                 MemberBinding memberBinding = Expression.Bind(item, property);
23                 memberBindingList.Add(memberBinding);
24             }
25             MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
26             Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
27             {
28                     parameterExpression
29             });
30             _FUNC = lambda.Compile();//拼装是一次性的
31         }
32         public static TOut Trans(TIn t)
33         {
34             return _FUNC(t);
35         }
泛型缓存

最终运行结果:

三. 剥离表达式目录树

   这里补充几个常用的表达式目录树的拼接代码:And、Or、Not 。

 ExpressionExtend扩展类代码:

 1   public static class ExpressionExtend
 2     {
 3         /// <summary>
 4         /// 合并表达式 expr1 AND expr2
 5         /// </summary>
 6         /// <typeparam name="T"></typeparam>
 7         /// <param name="expr1"></param>
 8         /// <param name="expr2"></param>
 9         /// <returns></returns>
10         public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
11         {
12             ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
13             NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);
14 
15             var left = visitor.Replace(expr1.Body);
16             var right = visitor.Replace(expr2.Body);
17             var body = Expression.And(left, right);
18             return Expression.Lambda<Func<T, bool>>(body, newParameter);
19 
20         }
21         /// <summary>
22         /// 合并表达式 expr1 or expr2
23         /// </summary>
24         /// <typeparam name="T"></typeparam>
25         /// <param name="expr1"></param>
26         /// <param name="expr2"></param>
27         /// <returns></returns>
28         public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
29         {
30 
31             ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
32             NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);
33 
34             var left = visitor.Replace(expr1.Body);
35             var right = visitor.Replace(expr2.Body);
36             var body = Expression.Or(left, right);
37             return Expression.Lambda<Func<T, bool>>(body, newParameter);
38         }
39         public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expr)
40         {
41             var candidateExpr = expr.Parameters[0];
42             var body = Expression.Not(expr.Body);
43 
44             return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
45         }
46     }

NewExpressionVisitor建立新表达式辅助类代码:

 1   /// <summary>
 2     /// 建立新表达式
 3     /// </summary>
 4     internal class NewExpressionVisitor : ExpressionVisitor
 5     {
 6         public ParameterExpression _NewParameter { get; private set; }
 7         public NewExpressionVisitor(ParameterExpression param)
 8         {
 9             this._NewParameter = param;
10         }
11         public Expression Replace(Expression exp)
12         {
13             return this.Visit(exp);
14         }
15         protected override Expression VisitParameter(ParameterExpression node)
16         {
17             return this._NewParameter;
18         }
19     }

如何使用的代码:

 1  public class VisitorUtils
 2     {
 3         public static void Show()
 4         {
 5             Expression<Func<User, bool>> lambda1 = x => x.userAge > 5;
 6             Expression<Func<User, bool>> lambda2 = x => x.id > 5;
 7             Expression<Func<User, bool>> lambda3 = lambda1.And(lambda2);
 8             Expression<Func<User, bool>> lambda4 = lambda1.Or(lambda2);
 9             Expression<Func<User, bool>> lambda5 = lambda1.Not();
10 
11             Do1(lambda1);
12             Do1(lambda2);
13             Do1(lambda3);
14             Do1(lambda4);
15             Do1(lambda5);
16         }
17 
18         private static void Do1(Expression<Func<User, bool>> func)
19         {
20             List<User> user = new List<User>()
21             {
22                 new User(){id=4,userName="123",userAge=4},
23                 new User(){id=5,userName="234",userAge=5},
24                 new User(){id=6,userName="345",userAge=6},
25             };
26 
27             List<User> peopleList = user.Where(func.Compile()).ToList();
28         }
29     }

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 

 

posted @ 2017-09-06 20:45  Yaopengfei  阅读(1147)  评论(1编辑  收藏