表达式树使用一种类似树的结构来表示代码,它的每个节点都是一个表达式,比如方法调用和x<y这样的二元运算等。我们可以对表达式树的内容进行编辑和运算,这样能够动态修改可执行代码,以及动态创建查询等。我们可以使用匿名lambda表达式或者C# API来创建表达式树。
这一系列文章,主要是对C#表达式树的一种总结,基本知识参考MSDN的内容 这部分内容可以直接到MSDN上查看,后面的几篇文章主要分享一下,在工作中碰到的应用到表达式树的部分,谨做为记录和分享。
生成表达式树
通过lambda表达式创建表达式树
可以通过将lambda表达式赋值给Expression<TDelegate>类型的变量,编译器可以自动生成创建该lambda表达式的表达式树。C#编译器只能从lambda表达式生成表达式树,只能是单行lambda表达式,不能解析多行lambda语句,如下,可以通过一下方式创建lambda表达式 num=>num<5的表达式树:
Expression<Func<int, bool>> lambda = num => num < 5;
通过API创建表达式树
使用API创建表达式,需要使用Expression类,这个类包含了创建特定类型表达式树节点的静态工厂方法,比如表示参数的变量ParameterExpression,表示方法调用的MethodExpression。ParameterExpression,MethodExpression以及其他特定的表达式类型都在System.Linq.Expression命名空间里定义,这些类型都派生于Expression抽象类。
下面的例子是使用API方式创建num=>num<5的lambda表达式对应的表达式树:
ParameterExpression numPara = Expression.Parameter(typeof(int), "num");//参数num ConstantExpression five = Expression.Constant(5, typeof(int));//常数5 BinaryExpression numLessThanFive = Expression.LessThan(numPara, five); Expression<Func<int, bool>> lambda1 = Expression.Lambda<Func<int, bool>>(numLessThanFive, new ParameterExpression[] { numPara });
从.NET Framework 4开始,表达式树API还支持赋值以及流程控制,比如循环,条件块和try ... catch块等。相对于通过lambda表达式创建表达式树,可以利用API创建更加复杂的表达式树,比如下面使用API创建数字阶乘的表达式树:
//参数value ParameterExpression value = Expression.Parameter(typeof(int), "value"); //本地变量 ParameterExpression result = Expression.Parameter(typeof(int), "result"); //标签,用来跳出循环 LabelTarget label = Expression.Label(typeof(int)); //创建表达式块 BlockExpression block = Expression.Block( //添加本地参数result new[] { result }, //result=1 赋值 Expression.Assign(result, Expression.Constant(1)), //循环 Expression.Loop( //循环条件 Expression.IfThenElse( //如果 value>1 Expression.GreaterThan(value, Expression.Constant(1)), //则 result*=value--; Expression.MultiplyAssign(result, Expression.PostDecrementAssign(value)), //否则跳出loop循环。跳到label的语句执行 Expression.Break(label, result) ), label ) ); //编译表达式树 Func<int, int> factor = Expression.Lambda<Func<int, int>>(block, value).Compile(); //执行,输出结果120 Console.WriteLine(factor(5));
解析表达式树
在获取了表达式树之后,如何获取表达式树的每一个部分,这个在有些情况下非常有用,下面这个例子展示了如何获取num=>num<5的各个部分。
Expression<Func<int, bool>> expreTree = num => num < 5; ParameterExpression param = (ParameterExpression)expreTree.Parameters[0];//num BinaryExpression operation = (BinaryExpression)expreTree.Body;//< ParameterExpression left = (ParameterExpression)operation.Left;//num ConstantExpression right = (ConstantExpression)operation.Right;//5 //output Decomposed expression: num => num LessThan 5 Console.WriteLine("Decomposed expression:{0} = > {1} {2} {3}", param.Name, left.Name, operation.NodeType, right.Value);
编译表达式树
Expression<TDelegate>类型有Compile方法,可以将表达式树编译成对应的TDelegate委托类型,使用方法如下:
// 创建表达式树 Expression<Func<int, bool>> expr = num => num < 5; // 将表达式树编译成对应委托 Func<int, bool> result = expr.Compile(); //调用委托方法,输出True Console.WriteLine(result(4)); //也可以直接编译后调用,输出True Console.WriteLine(expr.Compile()(4));
再比如,下面例子演示了,创建一个表达式树,然后编译执行:
//创建表达式树的执行逻辑 BinaryExpression be = Expression.Power(Expression.Constant(2D), Expression.Constant(3D)); //创建表达式树 Expression<Func<double>> le = Expression.Lambda<Func<double>>(be); //编译表达式树 Func<double> compileExpression = le.Compile(); //执行lambda表达式,获得结果8 double result = compileExpression(); Console.WriteLine(result);
表达式树的修改
表达式树是不可变对象(immutable),跟string类似,不能直接修改,只能复制一个然后重新构造。具体参考MSDN How to modify expression trees (C#).
结语
本篇全部内容参考MSDN上表达式树部分的内容,如果有基础建议直接看,这里只是个人作为笔记,也是表达式树的最基础部分,后文会介绍表达式树的一些用法。
以上就是C#表达式树的基本用法讲解的详细内容,更多关于C#表达式树的资料请关注自由互联其它相关文章!