C#表达式树

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wlk1229/article/details/80012079
C#表达式树



表达式和表达式树

表达式可以是一个参数(如参数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));
        }
    }

输出结果:

 


猜你喜欢

转载自blog.csdn.net/wlk1229/article/details/80012079
今日推荐