11拼接Expression表达式树

什么是表达式树

表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,它将我们原来可以直接由代码编写的逻辑以表达式的方式存储在树状的结构里,从而可以在运行时去解析这个树,然后执行,实现动态的编辑和执行代码。在.Net 里面的Linq to SQL就是对表达式树的解析。

表达式和表达式树,表达式相信大家都知道,比如x+5或者5,都可以算是表达式,而表达式树里面的树指的二叉树,也就是表达式的集合,C#中的Expression类就是表达式类。对于一棵表达式树,其叶子节点都是参数或者常数,非叶子节点都是运算符或者控制符。


表达式树主要由下面四部分组成:

1、Body 主体部分
2、Parameters 参数部分
3、NodeType 节点类型
4、Lambda表达式类型


初始化表达式树和初始化委托

初始化委托的时候,实际上就是定义了一个匿名方法,相当于在做事

如:
Func<string> func = () => "hello";

而反编译后可以看到,实际上系统也是定义了匿名方法
Func<string> func = delegate{return "hello"; };


初始化表达式的时候,他是一个表达式树,相当于在描述事物

Expression<Func<string>> func_expression = ()=>"hello world";

反编译后可以看到系统会帮我们拼接表达式

Expression<Func<string>> func_expression =
                Expression.Lambda<Func<string>>(Expression.Constant("hello world", typeof(string)), Array.Empty<ParameterExpression>());

手动拼接表达式树

系统拼接表达式也是从左边到右边依次拼接,以先算括号,然后再乘除和方法

表达式树常用对象:

	ParameterExpression:参数表达式,表示变量
	ConstantExpression:常量表达式,常量
	MemberExpression:成员表达式,类里的成员(属性、字段等)
	MemberBinding:成员绑定,描述 MemberInitExpression 对象中使用的绑定类型,用于初始化属性
	MemberInitExpression:成员初始化表达式,可以创建一个新的类型,然后初始化它的成员
	BinaryExpression:二元表达式
	
二元表达式BinaryExpression的常用节点类型(NodeType):
	Call:表示方法
	Add:+
	Multiply:*
	Subtract:-
	Divide:/
	Modulo:%

Expression里的常用方法 :
Compile():编译表达式


借助linqpad图示了解拼接过程

1,Expression<Func<int,int>> expression = (a) => a*2;

步骤1:
image

步骤2:
image


2,Expression<Func<int,int>> func_expression = (a,b) =>(a+b)*Convert.ToInt32(a)+b;

步骤1:
image

步骤2:
image

步骤3:
image

步骤4:
image

步骤5:
image

了解拼接过程我们就可以根据Expression的节点类型拼接常见的树了

1

Expression<Func<int>> expression = () => 10;

用反编译后我们发现和反编译后系统帮我们拼接的表达式基本一致的结果


            //用反编译可以看到系统帮我们生成的表达式
            Expression<Func<int>> expression2 =
            Expression.Lambda<Func<int>>(Expression.Constant(10, typeof(int)), Array.Empty<ParameterExpression>());



            //自己创建这个表达式
            //1、创建常量表达式
            ConstantExpression constant = Expression.Constant(10, typeof(int));
            //2、生成表达式
            Expression<Func<int>> myexpression = Expression.Lambda<Func<int>>(constant, null);//因为没有参数,所以直接给null即可

            //编译后调用
            Console.WriteLine(myexpression.Compile().Invoke());


2

Expression<Func<int, int>> expression = (a) => 10 + a;

 //用反编译查看系统帮我们生成的expression表达式树如下:
            ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "a");
            Expression<Func<int, int>> expression =
            Expression.Lambda<Func<int, int>>(Expression.Add(Expression.Constant(10, typeof(int)), parameterExpression), new ParameterExpression[1] { parameterExpression });




            //自己拼接

            //1、创建常量表达式
            ConstantExpression constant = Expression.Constant(10);//左边left

            //2、创建参数列表表达式,第一个参数,该参数列表的数据类型,第二个参数,参数名
            ParameterExpression parameterExp = Expression.Parameter(typeof(int), "a");//右边right

            //3、创建二元表达式(相当于结果body,这个里面就包括了left和right两个表达式),add方法需要两个表达式左边和右边的两个表达式
            BinaryExpression binaryExp = Expression.Add(constant, parameterExp);//Body

            //4、生成表达式,Lambda方法里需要给它泛型具体类型,然后参数里第一个参数是body,第二个是参数列表
            Expression<Func<int, int>> myExpression = Expression.Lambda<Func<int, int>>(binaryExp, new ParameterExpression[] { parameterExp });


            //编译后调用该表达式树为我们生成的委托
            Console.WriteLine(myExpression.Compile().Invoke(5));


3

Expression<Func<int, int, int>> expression = (y, x) => 5 + y * y - x;

 ////系统帮我们拼接的
            ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "y");
            ParameterExpression parameterExpression2 = Expression.Parameter(typeof(int), "x");

            Expression<Func<int, int, int>> expression =
                Expression.Lambda<Func<int, int, int>>(
                    Expression.Subtract(Expression.Add(Expression.Constant(5, typeof(int)), Expression.Multiply(parameterExpression, parameterExpression)), parameterExpression2),
                    new ParameterExpression[2] { parameterExpression, parameterExpression2 }
                );




            //1、写最里层的两个参数表达式,因为这一步先算两个参数相乘,但是两个参数名一样,所以这里定义一个参数表达式即可
            ParameterExpression parameterExpY = Expression.Parameter(typeof(int), "y");//右边right和左边left共用y

            //2、创建他们两个参数的二元表达式
            BinaryExpression binaryExpMultiply = Expression.Multiply(parameterExpY, parameterExpY);//right : y * y



            //3、开始拼接第2层,创建一个常量表达式5,第一个参数,值,第二个参数,类型
            ConstantExpression constant = Expression.Constant(5, typeof(int));//left : 5

            //4、创建一个二元表达式+,左边是常量,右边是上个二元表达式的结果
            BinaryExpression binaryExpAdd = Expression.Add(constant, binaryExpMultiply);//left : 5 + y * y


            //5、开始拼接第3层,创建一个参数表达式
            ParameterExpression parameterExpX = Expression.Parameter(typeof(int), "x");//right

            //5、创建二元表达式-
            BinaryExpression binaryExpSubtract = Expression.Subtract(binaryExpAdd, parameterExpX);//5 + y * y - x


            //6、最后将body和lambda组成一个表达式
            Expression<Func<int, int, int>> expression = Expression.Lambda<Func<int, int, int>>(binaryExpSubtract, new ParameterExpression[] { parameterExpY, parameterExpX });


            //编译后调用
            Console.WriteLine(expression.Compile().Invoke(5, 6));


4

Expression<Func<Student, string>> expression = (s) => s.Id.ToString();

            //1、创建参数表达式s
            ParameterExpression parameterExp = Expression.Parameter(typeof(Student), "s");//s,声明s的类型


            //2、创建成员表达式
            MemberExpression memberMxpression = Expression.Property(parameterExp, "Id");//s.Id,给s加一个属性Id


            //3、拼接ToString()方法,第一个参数,给那个成员调用该方法,第二个参数,调用方法名称,第三个参数 ,调用该方法的参数
            MethodCallExpression methodCallExpression = Expression.Call(memberMxpression, "ToString", null);//s.Id.ToString()

            //4、最后将body和lambda组成一个表达式
            Expression<Func<Student, string>> expression = Expression.Lambda<Func<Student, string>>(methodCallExpression, new ParameterExpression[] { parameterExp });


            //调用
            Console.WriteLine(expression.Compile().Invoke(new Student() { Id = 1, Name = "张三" }));
posted @ 2021-11-13 14:12  青仙  阅读(411)  评论(0编辑  收藏  举报