一. 基本介绍
回忆: 最早接触到表达式目录树(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
泛型缓存最终运行结果:

三. 剥离表达式目录树
这里补充几个常用的表达式目录树的拼接代码: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 }
https://www.cnblogs.com/yaopengfei/p/7486870.html
