表达式和表达式树
表达式可以是一个参数(如参数x),一个常数(如常数5),一个加运算(如x+5)等等,可以把几个小的表达式组装在一起成为大的表达式,例如:(x+5)-(++y)。对于这样一个表达式可以用一棵树来表示,如下:
这就是表达式树,表达式树本身也是一个表达式(大的表达式)。一个表达式也是一棵表达式树,可以说它是一棵小的表达式树。可以把表达式树和表达式认为是一个东西,C#中都用Expression类表示。
表达式树结构
对于一棵表达式树,其叶子节点都是参数或者常数,非叶子节点都是运算符或者控制符。
C#中的Expression类就是表达式类,在C#中表达式树的节点总共有85种,这些类型全部由枚举变量ExpressionType决定。Expression类成员变量NodeType记录了表达式树根节点的节点类型。表达式树还有一个值类型,由Expression类成员变量Type决定。例如表达式(x+5)-(++y) NodeType为ExpressionType.Subtract(减),Type为typeof(int)。
表达式树的创建
Lambda表达式方法
表达式可以通过Lambda表示创建Expression<TDelegate>类型,如下:
Expression<Func<int, int,bool>> fun = (x, y) => x < y
这种方法创建出的表达式根节点类型为ExpressionType.Lambda,Type类型为返回值类型typeof(bool)。
这里的表达式根节点类型为Lambda并不是LessThan(小于)。Expression<TDelegate>有一个成员变量Body,这个成员变量是“lambda表达式的式子”,在例子中fun.Body为x<y,fun.Body的节点类型为LessThan,这里的x<y只是fun的一棵子树。
组装法
另一种创建表示的方式是通过Expression类的静态函数“组装”表达式如下:
ParameterExpression p1 = Expression.Parameter(typeof(int),"x");
ParameterExpression p2 = Expression.Parameter(typeof(int),"y");
BinaryExpression expr =Expression.GreaterThan(p1, p2);
我们先创建了两个参数表达式x, y然后用GreaterThan组装在一起,最终的表达式为“x>y”,expr的节点类型为LessThan,Type类型为typeof(bool)
Expression类中有我们所需要所有“组装”表达式的用的静态函数,我们不能直接用new的方法去创建表达式节点,表达式所有的节点需要用Expression类中的静态函数创建。
更多关于如何创建表示请参考:
http://www.cnblogs.com/jesse2013/p/expressiontree-part1.html
http://www.cnblogs.com/jesse2013/p/expressiontree-part2.html
节点类与节点类型
从创建表达式可以发现表达式不同类型节点会对应不同的类,如ParameterExpression,BinaryExpression,Expression<TDelegate>等等,这些类都继承了Expression类,所以它们都表示表达式,只是他们的根节点的节点类型不一样。
表达式节点类和表达式类型是有一定对应关系的,下面有一部分总结但不完整。
类型 |
类名 |
可对应的节点类型 |
常量表达式 |
ConstantExpression |
ExpressionType.Constant |
参数表达式 |
ParameterExpression |
ExpressionType.Parameter |
一元运算符表达式 |
UnaryExpression |
ExpressionType.Negate ExpressionType.NegateChecked ExpressionType.Not ExpressionType.Convert ExpressionType.ConvertChecked ExpressionType.ArrayLength ExpressionType.Quote ExpressionType.TypeAs |
二元运算符表达式 |
BinaryExpression |
ExpressionType.Add ExpressionType.AddChecked ExpressionType.Subtract ExpressionType.SubtractChecked ExpressionType.Multiply ExpressionType.MultiplyChecked ExpressionType.Divide ExpressionType.Modulo ExpressionType.And ExpressionType.AndAlso ExpressionType.Or ExpressionType.OrElse ExpressionType.LessThan ExpressionType.LessThanOrEqual ExpressionType.GreaterThan ExpressionType.GreaterThanOrEqual ExpressionType.Equal ExpressionType.NotEqual ExpressionType.Coalesce ExpressionType.ArrayIndex ExpressionType.RightShift ExpressionType.LeftShift ExpressionType.ExclusiveOr |
is运算符表达式 |
TypeBinaryExpression |
ExpressionType.TypeIs |
条件表达式 |
ConditionalExpression |
ExpressionType.Conditional |
访问字段或属性表达式 |
MemberExpression |
ExpressionType.MemberAccess |
调用成员函数表达式 |
MethodCallExpression |
ExpressionType.Call |
Lambda表达式 |
LambdaExpression |
ExpressionType.Lambda |
委托表达式 |
Expression<TDelegate> |
ExpressionType.Lambda |
new运算符表达式 |
NewExpression |
ExpressionType.New |
new数组表达式 |
NewArrayExpression |
ExpressionType.NewArrayInit ExpressionType.NewArrayBounds |
调用其他表达式的表达式 |
InvocationExpression |
ExpressionType.Invoke |
属性初始表达式 |
MemberInitExpression |
ExpressionType.MemberInit |
列表初始表达式 |
ListInitExpression |
ExpressionType.ListInit |
表达式的运行
实际上大多数表达式都不能运行,只有LambdaExpression和Expression<TDelegate>表达式可以运行,因为这两种表达式相当于一个函数。
这两种表达式有一个成员函数Compile,可把表达式转换成一个Fun或Action函数对象。如下:
Expression<Func<int,int>> Fun = x => x + 6;
var f = Fun.Compile();
f(5);
其他类型表达式如果想要运行,我们可以通过Expression类的静态函数Lambda<TDelegate>转换成相应的Expression<TDelegate>节点,然后运行。如下运行我们自己组装的表达式:
ParameterExpressionp1 = Expression.Parameter(typeof(int), "x");
ParameterExpressionp2 = Expression.Parameter(typeof(int), "y");
BinaryExpressionexpr = Expression.GreaterThan(p1, p2);
Expression<Func<int, int, bool>>FunGreater =
Expression.Lambda<Func<int, int, bool>>(expr, p1, p2);
Console.WriteLine(FunGreater.ToString());
Console.WriteLine(FunGreater.Compile().Invoke(5, 2));
遍历表达式树
表达式树是一棵树,我们可以遍历这棵树,在遍历的时候我们可以替换掉原来表达式中的参数、操作、常数来构建一颗新的表达式树。
遍历表达式树
可以通过ExpressionVisitor类来遍历一棵表达式树。我们需要重写ExpressionVisitor类的一些函数来达到我们的目的如重写ExpressionVisitParameter(ParameterExpressionnode)函数,这个函数是当遍历到参数节点时调用。node参数是表达式中的参数,而我们的返回值将会替换当前的表达式中的参数。
遍历顺序
遍历的顺序可以由代码控制,可以是先遍历根节点再遍历子树,或者先遍历子树后遍历根节点,然后从左到右遍历子树。
以下创建了一个先序遍历的类,即先遍历根节点,然后从左到右遍历子树。
internal class ExpVisitor : ExpressionVisitor
{
protected override Expression VisitBinary(BinaryExpression node)
{
Console.WriteLine($"Binary Type = {node.NodeType}");
return base.VisitBinary(node);//继续遍历节点的子树
}
protected override Expression VisitConstant(ConstantExpression node)
{
Console.WriteLine($"Constant = {node.Value}");
return node;//因为是叶子节点,所以可以直接返回
}
protected override Expression VisitParameter(ParameterExpression p)
{
Console.WriteLine($"ParamName = {p.Name}");
return p;//因为是叶子节点,所以可以直接返回
}
}
利用上面的遍历类遍历表达式 (x <10 && x<3) || (y >= z)
遍历的结果如下:
替换参数
以下是一个替换参数的遍历类
internal class ExpressionParameterReplacer : ExpressionVisitor
{
public ExpressionParameterReplacer()
{
var newParam = Expression.Parameter(typeof(int), "NewParam");
this.ParameterExpression = newParam;
}
public ParameterExpression ParameterExpression { get; private set; }
public Expression Replace(Expression expression)
{
return this.Visit(expression);
}
protected override Expression VisitParameter(ParameterExpression _)
{
return this.ParameterExpression;
}
}
使用该类替换表达式(x <10 && x<3) || (y >= z)中所有的参数,然后遍历输出,替换后生成的表达式为(NewParam < 10&& NewParam <3)|| (NewParam >= NewParam),三个参数都变成了NewParam。
结果如下:
两个例子完整的代码如下:
internal class ExpressionParameterReplacer : ExpressionVisitor
{
public ExpressionParameterReplacer()
{
var newParam = Expression.Parameter(typeof(int), "NewParam");
this.ParameterExpression = newParam;
}
public ParameterExpression ParameterExpression { get; private set; }
public Expression Replace(Expression expression)
{
return this.Visit(expression);
}
protected override Expression VisitParameter(ParameterExpression _)
{
return this.ParameterExpression;
}
}
internal class ExpVisitor : ExpressionVisitor
{
protected override Expression VisitBinary(BinaryExpression node)
{
Console.WriteLine($"Binary Type = {node.NodeType}");
return base.VisitBinary(node);//继续遍历节点的子树
}
protected override Expression VisitConstant(ConstantExpression node)
{
Console.WriteLine($"Constant = {node.Value}");
return node;//因为是叶子节点,所以可以直接返回
}
protected override Expression VisitParameter(ParameterExpression p)
{
Console.WriteLine($"ParamName = {p.Name}");
return p;//因为是叶子节点,所以可以直接返回
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"Visiter Tree:");
Expression<Func<int, int, int, bool>> FunX1 = (x, y, z) => (x < 10 && x>3) || (y >= z);
Console.WriteLine(FunX1.Body.ToString());
ExpVisitor v2 = new ExpVisitor();
v2.Visit(FunX1.Body);
Console.WriteLine($"\n\nReplace Param:");
ExpressionParameterReplacer v = new ExpressionParameterReplacer();
var temp = v.Visit(FunX1.Body);
Console.WriteLine(temp.ToString());
v2.Visit(temp);
Expression<Func<int, bool>> ReplaceExp = Expression.Lambda<Func<int, bool>>(temp, v.ParameterExpression);
ReplaceExp.Compile().Invoke(5);
Console.WriteLine($"\n\n");
ParameterExpression p1 = Expression.Parameter(typeof(int), "x");
ParameterExpression p2 = Expression.Parameter(typeof(int), "y");
BinaryExpression expr = Expression.GreaterThan(p1, p2);
Expression<Func<int, int, bool>> FunGreater = Expression.Lambda<Func<int, int, bool>>(expr, p1, p2);
Console.WriteLine(FunGreater.ToString());
Console.WriteLine(FunGreater.Compile().Invoke(5, 2));
}
}
输出结果: