说起Lambda表达式,大家基本都很熟悉了,而表达式树(Expression Trees),则属于80%的工作中往往都用不到的那种技术,所以即便不是什么新技术,很多人对其理解都并不透彻。此文意图从表达式树基本技术点结合实际应用,逐步来看表达式树究竟是怎么一回事,希望能帮助读者彻底学会表达式树^_^

一、初见表达式树Expression<TDelegate>

不妨回想一下,你第一次使用表达式树是在哪里呢,比如Expression<TDelegate>?

比如在linq查询中,常常用到Where方法过滤,OrderBy排序等,调用的是IQueryable<TSource>的静态扩展类Queryable的静态扩展方法。

 1 // C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\System.Core.dll
 2 namespace System.Linq
 3 {
 4     //
 5     // 摘要:提供一组用于查询实现 System.Linq.IQueryable`1 的数据结构的 static方法。
 6     //     
 7     public static class Queryable
 8     {
 9         //...
10         public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
11         public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector);
12         //...
13     }
14 }

可以看到里面的参数类型Expression<Func<TSource, bool>> predicate和Expression<Func<TSource, TKey>> keySelector,咦?这不就是表达式树(Expression<TDelegate>)吗?!是的,但不止如此。

Expression<TDelegate>是表达式树的一种,并不等同于表达式树。

二、从Expression<TDelegate>到LambdaExpression

看下面的代码:

1  Func<Person, bool> func1 = x => x.Age > 18;
2  Func<Person, string> func2 = x => x.Name;
3  Expression<Func<Person, bool>> expression1 = x => x.Age > 18;
4  Expression<Func<Person, string>> expression2 = x => x.Name;
5  //错误CS0834,无法将具有语句体的lambda表达式转换为表达式树
6  Func<Person, int, int> func3 = (x, y) => { return x.Age + y; };
7  Action<Person, int> func4 = (x, y) => { };
8  Expression<Func<Person, int, int>> expression3 = (x, y) => { return x.Age + y; };
9  Expression<Action<Person, int>> expression4 = (x, y) => { };

expression1和expression2可以编译通过,expression3和expression4则编译错误,提示"无法将具有语句体的Lambda表达式转换为表达式树 "。

这是因为,我们可以将Lambda表达式赋值给泛型表达式类型变量(Expression<TDelegate>),编译器会将我们的Lambda表达式编译为表达式树,但是仅限于expression lambdas(表达式Lambdas,也叫做single-line lambdas,即不具有语句体的Lambda表达式),相对的statement lambdas(语句Lambdas,也叫做multi-line lambdas),编译器则会报错。

看看反编译后的func1,func2,expression1,expression2。

    ParameterExpression expression3;
    Func<Person, bool> func = x => x.Age > 0x12;
    Func<Person, string> func2 = x => x.Name;
    ParameterExpression[] parameters = new ParameterExpression[] { expression3 };
    Expression<Func<Person, bool>> expression = Expression.Lambda<Func<Person, bool>>(Expression.GreaterThan(Expression.Property(expression3 = Expression.Parameter(typeof(Person), "x"), (MethodInfo) methodof(Person.get_Age)), Expression.Constant(0x12, typeof(int))), parameters);
    ParameterExpression[] expressionArray2 = new ParameterExpression[] { expression3 };
    Expression<Func<Person, string>> expression2 = Expression.Lambda<Func<Person, string>>(Expression.Property(expression3 = Expression.Parameter(typeof(Person), "x"), (MethodInfo) methodof(Person.get_Name)), expressionArray2);

expression1和expression2变成了一堆复杂的语句,这才是它们作为表达式树原有的面目^_^

而Expression<TDelegate>来自何方?它继承自LambdaExpression。

来看看Expression<TDelegate>反编译后的构造函数

 1 [__DynamicallyInvokable]
 2 public sealed class Expression<TDelegate> : LambdaExpression
 3 {
 4     // 构造函数
 5     internal Expression(Expression body, string name, bool tailCall, ReadOnlyCollection<ParameterExpression> parameters) : 
 6    base(typeof(TDelegate), name, body, tailCall, parameters)
 7     {
 8     }
 9     //...
10 }

构造函数什么也没有做,只是将参数传递给父类构造函数,所以可以认为Expression<TDelegate>是对LambdaExpression的一层封装,而LambdaExpression又是继承自Expression,是表达式树的一种,而且是最特别的一种,为何特别,且看下文^_^

三、何为表达式树

首先来看下面的类图,可以更直观看到Expression<TDelegate>、LambdaExpression、Expression三者的关系。

 

不可不知的表达式树(1)Expression初探

黄线表示,使用表达式Lambdas赋值给Expression<TDelegate>类型变量,TDelegate会赋值给父类LambdaExpression的Type属性(联系下前面讲的构造函数的事情 ^_^ )

重点看绿线,LambdaExpression继承自Expression,而LambdaExpression的Body属性,又是Expression类型。

哇哦,眼尖的你注意到LambdaExpression还有个Compile()方法,它会编译Body,生成表示 lambda 表达式的可执行委托,然后你就可以调用委托了,所以真正的表达式树所谓的树,就是藏在Body里啦

So,其实Expression的子类型多达几十种,例如ParameterExpression,BinaryExpression,MethodCallExpression等等,但是只有LambdaExpression(Lambda表达式树)可以执行!

如果你还有疑问,那么任意一棵其它类型的表达式树,怎样执行呢?答案自然是创建一个新的LambdaExpression了,将表达式树作为LambdaExpression的Body,然后你懂的^_^

具体可以调用这个方法 public static Expression<TDelegate> Lambda<TDelegate>(Expression body, params ParameterExpression[] parameters);

此时,我们再来解答最初的疑问,究竟何为表达式树(Expression Trees)?

表达式树即一份树形结构存储的代码,树的每个结点又都是一个表达式树,你可以编译然后运行这份树形代码。

那么可以用它做什么呢?

  1. 表示Lambda表达式,这个显而易见了;
  2. 修改可执行代码,创建动态查询;
  3. 定制自己的IQueryable,通过翻译表达式树数据结构为特定的查询语言,实现对特定数据源的查询,即定制orm。与我们对各种数据库的Linq查询同理;

也就是说小到根据字段字符串参数的过滤排序等,大到定制自己的orm,你都离不开表达式树。

下篇文章中,我们将就表达式树的具体用途,来做实例展示。

四、手动创建表达式树

可以通过哪些途径来创建表达式树呢?通过上文我们知道可以使用表达式Lambda由编译器来创建Lambda表达式树,例如Expression<Func<int, bool>> lambda = num => num < 5;

而另外一个更通用的方式,就是引入System.Linq.Expressions命名空间,手动构造每一个表达式树结点,来创建表达式树。

来看几个简单的例子,一看就懂。

 1 class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             var ints = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 6             Func1(ints);
 7             Func2(ints);
 8             Func3(ints);
 9             Console.Read();
10         }
11         /// <summary>
12         /// 目标表达式树 x => x > 5 && x <= 7
13         /// </summary>
14         public static void Func1(int[] ints)
15         {
16             //创建参数x
17             var parameter = Expression.Parameter(typeof(int), "x");
18             //创建表达式x>5
19             var con1 = Expression.Constant(5);
20             var bin1 = Expression.GreaterThan(parameter, con1);
21             //创建表达式x<=7
22             var con2 = Expression.Constant(7);
23             var bin2 = Expression.LessThanOrEqual(parameter, con2);
24             //组合两个表达式
25             var body = Expression.And(bin1, bin2);
26             //获取Lambda表达式
27             var lambda = Expression.Lambda<Func<int, bool>>(body, parameter);
28             var ints2 = ints.Where(lambda.Compile());
29             //执行结果
30             Console.WriteLine("\r\n Func1构造的表达式树Body:\r\n {0}", body);
31             Console.WriteLine("\r\n Func1构造的表达式树:\r\n {0}", lambda);
32             Console.WriteLine("\r\n Func1的结果:\r\n {0}", string.Join(",", ints2));
33         }
34         /// <summary>
35         /// 目标表达式树 x => x % 2 == 0 ? x : 0
36         /// </summary>
37         public static void Func2(int[] ints)
38         {
39             //创建参数 x
40             var parameter = Expression.Parameter(typeof(int), "x");
41             //创建表达式 x % 2
42             var con1 = Expression.Constant(2);
43             var bin1 = Expression.Modulo(parameter, con1);
44             //创建表达式 (x % 2) == 0
45             var con2 = Expression.Constant(0);
46             var bin2 = Expression.Equal(bin1, con2);
47             //创建表达式 x % 2 == 0 ? x : 0
48             var body = Expression.Condition(bin2, parameter, Expression.Constant(0));
49             //获取Lambda表达式
50             var lambda = Expression.Lambda<Func<int, int>>(body, parameter);
51             var ints2 = ints.Select(lambda.Compile());
52             //执行结果
53             Console.WriteLine("\r\n Func2构造的表达式树Body:\r\n {0}", body);
54             Console.WriteLine("\r\n Func2构造的表达式树:\r\n {0}", lambda);
55             Console.WriteLine("\r\n Func2的结果:\r\n {0}", string.Join(",", ints2));
56         }
57         /// <summary>
58         /// 目标表达式树 x => Console.WriteLine(x)
59         /// </summary>
60         /// <param name="ints"></param>
61         public static void Func3(int[] ints)
62         {
63             //创建参数i
64             var parameter = Expression.Parameter(typeof(int), "x");
65             //获取Console.WriteLine MethodInfo
66             MethodInfo method = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) });
67             //创建表达式
68             var body = Expression.Call(method, parameter);
69             var lambda = Expression.Lambda<Action<int>>(body, parameter);
70             Console.WriteLine("\r\n Func3构造的表达式树Body:\r\n {0}", body);
71             Console.WriteLine("\r\n Func3构造的表达式树:\r\n {0}", lambda);
72             Array.ForEach(ints, lambda.Compile());
73         }
74     }
View Code

相关文章:

  • 2021-08-16
  • 2022-12-23
  • 2022-12-23
  • 2022-02-25
  • 2021-10-14
  • 2021-08-28
猜你喜欢
  • 2022-12-23
  • 2021-08-08
  • 2022-12-23
  • 2021-12-31
  • 2022-12-23
  • 2022-12-23
  • 2021-06-02
相关资源
相似解决方案