表达式树(Expression Tree)

时间:2022-08-29 23:10:19

你每创建一个表示表达式的实例时,都可以将该类型实例看成是一棵表达式树。每种表示表达式的类型都有一个具体的类型,如Expression的Variable()方法创建的是ParameterExpression类型的表达式,Expression的Add()方法创建的则是BinaryExpression类型的表达式。无论哪种表示表达式的类型都是从Expression派生。

//使用Expression的静态方法创建表达式
ParameterExpression variable = Expression.Variable ( typeof ( int ) , "x" ); // x

LambdaExpression(Lambda表达式类)

LambdaExpression是Expression的子类,此类型表示一个Lambda表达式类型。

//使用Expression的静态方法创建表达式
LambdaExpression lambda = Expression.Lambda ( Expression.Variable ( typeof ( int ) , "x" ) ); //()=>x

Expression<TDelegate>(表达式委托类)

此类从LambdaExpression派生,所以也可以使用此类来创建一个表示Lambda表达式的类型,这种方式只能提供一个Lambda表达式,不能提供Lambda语句。

//直接将Lambda表达式表示为一个LambdaExpression
Expression<Func<int,int>> Info =  ( x ) => x; //linq查询方法中的参数就是类似于这种将lambda表达式隐式转换为了lambda表达式树类型

执行表达式

假设创建了一个表示加法运算的Lambda表达式对象,现在想要执行这个表示Lambda表达式的对象,此时就要用到Expression<TDelegate>类,因为此类从LambdaExpression派生,LambdaExpression提供了一个叫做Compile()的方法可以将表示Lambda表达式的对象(LambdaExpression)解析为一个委托,然后你就可以执行Lambda表达式。而要执行表示表达式的对象,也必然需要将其封装到Lambda表达式中,否则不可能执行。封装任何表示表达式的对象都是通过Expression的Lambda<TDelegate>(Expression expr)方法,该方法是非泛型版本Expression.Lambda ( Expression expr )的重载,返回一个Expression<TDelegate>类型的实例。其中TDelegate是一个委托类型,你可以根据封装的表示lambda表达式的对象的代码逻辑来确定需要为Lambda定义一个什么样的委托,可以使用内置的Fun或Action委托表示该LambdaExpression。比如Expression<Func<TDelegate>> | Expression<Action<TDelegate>> | Expression<Action>。只要记住:要执行一个表示表达式的对象就需要使用Expression的Lambda<TDelegate>(Expression expr)方法对其进行封装,使其处于Lambda方法体中以便可以被执行。


var x=Expression.Parameter ( typeof ( int ) , "x" ); //表示定义参数的Expression表达式
var y = Expression.Parameter ( typeof ( int ) , "y" ); //表示定义参数的Expression表达式
var add = Expression.Add ( x , y ); //表示加法运算的Expression表达式 //将表达式封装到表示Lambda的表达式中,因为add是表示计算x+y的表达式,所以Lambda<TDelagate>中的委托应定义为Lambda<Func<int,int,int>>
var lambdaInfo =Expression.Lambda<Func<int,int,int>> ( add, new [ ] { x, y } ); int r = lambdaInfo.Compile ( ) (1,2); //执行
Console.WriteLine ( r ); // print 3

表达式树

无论使用哪种方式创建表达式对象,编译器都会自动为表达式生成一棵树结构,然后将表达式主体的代码体拆分成单一的表达式并作为主体表达式的子节点。变量、参数、运算符都会被拆分成一个单一的表达式,如果被拆分的表达式含有多个子表达式,则子表达式将作为表达式的子节并以此类推。上面第三个例子中创建了一个表示Lambda的表达式,该表达式接收并返回一个int类型的变量,其树结构如下:

表达式树(Expression Tree)

Expression<Func<int , int , bool>> additionExpressionInfo = ( x , y ) => x != y && x != 0;

上面例子中创建了一个表达式( x , y ) => x != y && x!=0,代码体是x != y && x!=0,&&是一个表达式,它含有左右两个操作数的子表达式,所以它会被拆分,左边x != y是一个表达式,右边 x!=0也是一个表达式,左右两边都含有子表达式,所以会继续拆分,直到无法拆分为止,结构如下:

表达式树(Expression Tree)

Expression的子类

树的每个子节点是一个具体的表达式,它们都有自己的表达式类型,这些类型从Expression派生。


UnaryExpression; //一元运算表达式
BinaryExpression; //二元运算表达式
ConstantExpression; //常量表达式
ParameterExpression; //变量、变量参数表达式
GotoExpression; //跳转语句表达式,如:return。continue、break
BlockExpression; //块语句表达式
ConditionalExpression; //条件语句表达式
LoopExpression;  //循环语句表达式
SwitchExpression; //选择语句表达式
IndexExpression; //访问数组索引表达式
MethodCallExpression; //调用方法表达式
LambdaExpression;  //Lambda表达式
TypeBinaryExpression; //类型检查表达式
NewArrayExpression;  // 创建数组表达式
DefaultExpression; //默认值表达式
DynamicExpression; //动态类型表达式
TryExpression; //try语句表达式
MemberExpression; //类成员表达式
InvocationExpression; //执行Lambda并传递实参的表达式
NewExpression; //调用无参构造函数表达式
MemberInitExpression; //调用带参构造函数表达式,可初始化成员
ListInitExpression; //集合初始化器表达式

ExpressionType枚举

表示乘法运算和表示加法运算的表达式都是属于二元运算,所以这两种表达式都是BinaryExpression类型,但如果需要确定这两个表达式具体的行为,就需要使用Expression.NodeType属性,此属性是一个枚举数,用以获取表达式属于什么种类(根据其行为判定)。

 /*节点的NodeType用以描述表达式的行为*/

表达式类型的转换

所有表达式类型都从Expression类派生,当创建一棵表达式树时,如果创建的是一个Lambda表达式,那么得到的是一个Expression<TDelegate>实例,Expression<TDelegate>的Body属性存储Lambda封装的表达式,比如Expression<Func<int,int>> info=(x)=>x,其Body返回x,x是一个ParameterExpression,但Body被表示为表达式基类Expression,其实际存储的是ParameterExpression:


Expression<Func<int , int>> ExpressionInfo = ( x ) => x; // => x 被解析为ParameterExpression
Console.WriteLine ( ExpressionInfo.Body.GetType().Name ); //ParameterExpression
var pa = ExpressionInfo.Body as ParameterExpression; //可以将父类转子类,因为Body虽然是Expression类型,但实际存储的是子类实例
Expression<Func<int , int , bool>> additionExpressionInfo = ( x , y ) => x != y && x != 0;
Console.WriteLine ( additionExpressionInfo.Body.GetType().Name );  //BinaryExpression
var add = additionExpressionInfo.Body as BinaryExpression; //可以将父类转子类,因为Body虽然是Expression类型,但实际存储的是子类实例

何时使用Expression<TDelegate>?

现在假设你要创建一个表示调用string的EndWith()方法的表达式,如果使用Expression的静态方法,你得创建一堆子表达式,然后将它们合成为一个表示方法调用的表达式,再将方法表达式、方法需要的参数表达式全部封装到LambdaExpression中,以便可以执行


var x = Expression.Parameter ( typeof ( string ) , "x" );
var y = Expression.Parameter ( typeof ( string ) , "y" );
var methodInfo = typeof ( string ).GetMethod ( "StartsWith" , new Type [ ] { typeof ( string ) } );
var call = Expression.Call ( x , methodInfo , y );
var lambda = Expression.Lambda<Func<string , string , bool>> ( call , new [ ] { x , y } );
Console.WriteLine ( lambda.ToString ( ) ); // (x,y)=>x.StartsWith(y)
bool IsTrue = lambda.Compile ( ) ( "ABC" , "A" );
Console.WriteLine ( IsTrue ); //print true

一个简单的方法调用需要写出一堆臃肿难堪的代码?像这样简单的运算完全可以直接使用Expression<TDelegate>类型,因为你只需要创建一个Lambda表达式,一个Lambda表达式本身就可以被看成是一个Expression<TDelegate>,这样你完全不需要使用Expression的静态方法创建那么多表达式,一切交给Lambda表达式即可:

//产生与上面代码完全一样的表达式树
Expression<Func<string , string , bool>> lambdaExpr = ( str1 , str2 ) => str1.StartsWith ( str2 );
IsTrue=lambdaExpr.Compile ( ) ( "ABC" , "A" );
Console.WriteLine ( IsTrue ); //print true

前面说过Expression<TDelegate>只能表示一个Lambda表达式,不能表示Lambda语句。也即类似一元、二元运算的表达式完全可以使用Lambda来创建一个Expression<TDelegate>,而像条件判断、循环等语句表达式就不能使用Lambda表达式来创建,它们只能是Lambda语句,而Lambda语句不被视为Expression<TDelegate>,此时才需要考虑使用Expression的静态方法来构造更复杂的表达式逻辑。

LambdaExpression的方法


Body
//获取Lambda的方法体所表示的表达式 Parameters
//获取Lambda表达式的参数,返回一个ReadOnlyCollection<ParameterExpression> 集合,该集合存储了每一个参数变量表达式,可通过索引对项进行检索
//示例:
Expression<Func<int , int , bool>> additionExpressionInfo = ( x , y ) => x != y && x != 0;
foreach (var pa in additionExpressionInfo.Parameters )
{
    Console.WriteLine(pa.Name );
}
 
NodeType
//节点的类型,一个ExpressionType枚举数,用来描述表达式的行为 ReturnType
//Lambda表达式的返回类型  Type
//返回Expression<TDelegate>类型,
//示例:
Expression<Func<int , int , bool>> additionExpressionInfo = ( x , y ) => x != y && x != 0;
Console.WriteLine(expressionInfo.Type ); //print System.Func`2[System.Int32,System.Int32] Compile ( )
//将LambdaExpression封装的表达式生成委托返回
//示例:
Expression<Func<int , int , bool>> additionExpressionInfo = ( x , y ) => x != y && x != 0;
Func<int , int , bool> funcDeleg = additionExpressionInfo.Compile ( );
Console.WriteLine(funcDeleg ( 1 , 2 ) ); //print true
//或
bool IsTrue = additionExpressionInfo.Compile ( ) ( 1 , 2 ); // true

Expression的静态方法


Constant ( )
//创建一个常量表达式,注:Expression会自动生成常量的名字
//示例:
//创建一个表示常量的表达式:string x="寒食"
Expression.Constant( "寒食",typeof(string) )   Variable ( )
//创建一个ParameterExpression表示变量/参数变量表达式 Parameter ( )
//创建一个ParameterExpression表示变量/参数变量表达式
//示例:
Expression.Variable( typeof (int ) , "x" ); /*等同于*/ Expression.Parameter( typeof (int ) , "y" ); PostIncrementAssign ( )
//创建一个UnaryExpression表示++后置,类似的有PostDecrementAssign表示--后置 PreIncrementAssign ( )
//创建一个UnaryExpression表示++前置,类似的有PreDecrementAssign表示--前置 Assign ( )
//创建一个BinaryExpression表示赋值表达式,赋值表达式总是有左右两边的操作数
//示例:
BinaryExpression binaryExpression = Expression.Assign ( Expression.Variable ( typeof ( int ) , "x" ) , Expression.Constant ( 2 ) ); //左是x,右是2 int x=2
Console.WriteLine(binaryExpression.ToString ( ) ); //print int x=2 Add ( )
//创建一个BinaryExpression表示加法运算,表示左边操作数+右边操作数。类似的有AddAssign ( )表示左边操作数=左边操作数+右边操作数
//类似的有:Subtract(减)、Divide(除法)、Power(求幂)、Modulo(求余),也都有类似的加上Assign后缀的方法
//示例:
var s = Expression.Add ( Expression.Parameter ( typeof ( int ) , "x" ) , Expression.Constant ( 1 ) ).ToString ( ); //x+1
Console.WriteLine(s ); //x+1
var s2 = Expression.AddAssign ( Expression.Parameter ( typeof ( int ) , "x" ) , Expression.Constant ( 1 ) ).ToString ( );
Console.WriteLine(s2 );//x+=1 Lambda ( )
//创建一个LambdaExpression表示Lambda表达式 Lambda<TDelegate>(Expression runExpr, Expression runExprParameter)
//创建一个Expression<TDelagate>实例表示Lambda表达式,与非泛型版本的Lambda()方法相比,此方法显示指定了与Lambda对应的委托,执行Lambda时输入、输出参数会一目了然
//runExpr :表示被封装到Lambda的表达式,runExprParameter :Lambda接收的参数表达式 Call ( Expression expression , MethodInfo method , params Expression [ ] methodParamters )
//创建一个MethodCallExpression表示调用某个方法的表达式,只有表示方法调用的表达式和块表达式可以执行
//方法调用有两种情况:1.对象调用方法 2.类型调用方法 比如:Animal a=new Animal(); a.Show()区别于Animal.Count()
//如果不是对象调用方法则第一个参数可提供null,否则第一个参数需要提供调用方法的对象,对象也必须是一个Expression
//示例:
//假设要为这段代码创建表达式树:Console.WriteLine( ),
MethodCallExpression method = Expression.Call (
    null , //无实例调用方法
    typeof ( Console ).GetMethod ( "WriteLine" , new Type [ ] { typeof ( string ) } ) , //方法调用的表达式
    Expression.Constant ( "寒食" , typeof ( string ) ) //方法的参数
); Expression<Action> action = Expression.Lambda<Action> ( method );
action.Compile( ) ( ); // print 寒食
 
//假设要为这段代码创建表达式树:"hello world".ToUpper(),
Expression callExpr = Expression.Call (
    Expression.Constant ( "hello world" ) , //有实例调用方法
    typeof ( string ).GetMethod ( "ToUpper" , new Type [ ] { } )
);
string newStr = Expression.Lambda<Func<string>> ( callExpr ).Compile ( ) ( ); // HELLO WORLD Block ( )
//创建一个BlockExpression表示块表达式,此方法的最后一个参数表达式的值将会自动作为返回的结果
//示例:块中的表达式都是一步一步的定义出来的,创建块表达式时你可以想象一下在块中写C#代码块的流程,这样你就知道下面Block ( )方法的参数(表达式)是如何创建的了
ParameterExpression x = Expression.Parameter ( typeof ( int ) , "x" ); // int x
ParameterExpression y = Expression.Parameter ( typeof ( int ) , "y" ); // int y
BlockExpression block = Expression.Block (
    new ParameterExpression [ ] { x , y } , // int x,int y 定义块作用域中的变量表达式
    Expression.Assign ( x , Expression.Constant ( 100 ) ) , //x=100 定义块作用域中的赋值表达式
    Expression.Assign ( y , Expression.Constant ( 200 ) ) , //y =200 定义块作用域中的赋值表达式
    Expression.AddAssign ( x , y ) // var r = x + y ,r将自动作为块的返回结果
);
Func<int> func = Expression.Lambda<Func<int>> ( block ).Compile ( );
Console.WriteLine(func ( ) ); // print 300 //BlockExpression是一个块语句,相当于函数,
//如果块要接收参数,比如外部调用时传递实参,则不要在块中使用new ParameterExpression[ ] { } 声明同名的变量表达式,否则会覆盖掉参数
//示例:
var name = Expression.Parameter ( typeof ( string ) , "name" );
var function = Expression.Block (
    new ParameterExpression [ ] { name } , //覆盖掉了块的参数name
    name //返回的是块内部定义的name表达式
); string s = Expression.Lambda<Func<string , string>> ( function , name ).Compile ( ) ( "sam" );
Console.WriteLine(s ); // print "" var name = Expression.Parameter ( typeof ( string ) , "name" );
var function = Expression.Block (
    name
); string s = Expression.Lambda<Func<string , string>> ( function , name ).Compile ( ) ( "sam" );
Console.WriteLine(s ); // print sam
 
LessThanOrEqual
//创建一个BinaryExpression表示<=的表达式,类似的有LessThan ( ) GreaterThanOrEqual ( )
//创建一个BinaryExpression表示>=的表达式,类似的有GreaterThan ( )
//示例:
var x = Expression.Parameter ( typeof ( int ) , "x" );
var y = Expression.Parameter ( typeof ( int ) , "y" ); var block = Expression.Block (
    new ParameterExpression [ ] { x , y } ,
    Expression.Assign ( x , Expression.Constant ( 100000 ) ) ,
    Expression.Assign ( y , Expression.Constant ( 200 ) ) ,
    Expression.LessThanOrEqual ( x , y )  // x >= y
); bool IsTrue = Expression.Lambda<Func<bool>> ( block ).Compile ( ) ( );
Console.WriteLine(IsTrue ); // print true IfThenElse ( Expression expressionForTest , Expression ifTestIsTrue , Expression ifTestIsFlase )
//创建一个ConditionalExpression表示条件语句表达式
//示例:
var x = Expression.Parameter ( typeof ( int ) , "x" );
var y = Expression.Parameter ( typeof ( int ) , "y" ); var block = Expression.Block (
    new ParameterExpression [ ] { x , y } ,
    Expression.Assign ( x , Expression.Constant ( 100000 ) ) ,
    Expression.Assign ( y , Expression.Constant ( 200 ) ) ,
    Expression.IfThenElse (
        Expression.GreaterThanOrEqual ( x , y ) , // if ( x >= y )
        Expression.Call ( null , typeof ( Console ).GetMethod ( "WriteLine" , new Type [ ] { typeof ( string ) } ) , Expression.Constant ( "x>y==true" ) ) , //条件为真时执行
        Expression.Call ( null , typeof ( Console ).GetMethod ( "WriteLine" , new Type [ ] { typeof ( string ) } ) , Expression.Constant ( "x>y==false" ) ) //条件为假时执行
    )
); Expression.Lambda<Action>(block ).Compile ( ) ( ); // print x>y==true Label ( Type type )
//创建一个LabelTarget表示标签,此标签常用于退出语句块的标志,将标签作为Loop ( )方法的最后一个参数,然后在某个条件表达式中使用Expression.Break(LabelTarget )退出循环
//此方法可接收一个可选的Type类型的参数,在循环中假设循环退出时可以将某个值输出到标签中,这样你可以在外部拿到这个值
//参看下面的Loop语句表达式的示例,示例使用了Label ( )方法创建标签 Label ( LabelTarget label , ParameterExpression defaultValue )
//创建一个LabelExpression表示与LabelTarget关联的终止执行表达式,常用语在块中结合Expression.Return()方法使用
//参看下面的Return语句表达式的示例,示例使用了Label ( )方法创建标签 Break ( )
//创建一个GotoExpression表示退出循环,如果有嵌套循环,嵌套的循环内也得使用此方法来退出嵌套循环
//此方法可接收一个LabelTarget和一个可选的值参,退出循环时,值参会赋给标签,以便拿到这个值 Return ( )
//创建一个GotoExpression表示退出循环、退出方法体、退出块
//示例:
ParameterExpression x = Expression.Parameter ( typeof ( int ) );
LabelTarget label = Expression.Label ( typeof ( int ) );
BlockExpression block = Expression.Block (
     //如果x==1
     Expression.IfThen (
         Expression.Equal ( x , Expression.Constant ( 1 ) ) ,
         Expression.Block (
             Expression.Assign ( x , Expression.Constant ( 100 ) ) ,
             Expression.Return ( label , x ) //直接跳转到与label标签关联的LabelExpression表达式
         )
     ) ,
     //如果x==2
     Expression.IfThen (
         Expression.Equal ( x , Expression.Constant ( 2 ) ) ,
         Expression.Block (
             Expression.Assign ( x , Expression.Constant ( 200 ) ) ,
             Expression.Return ( label , x ) //直接跳转到与label标签关联的LabelExpression表达式
         )
     ) ,
     //与label标签关联的LabelExpression表达式,
     //无论两个IfThen表达式执行与否,此LabelExpressio始终会执行,
     //如果是这样,那么label就没有默认值,所以需要为label提供一个默认值Expression.Constant(300) 
     Expression.Label ( label , Expression.Constant ( 300 ) )
); int r = Expression.Lambda<Func<int , int>> ( block , x ).Compile ( ) ( 2 ); //Lambda方法的参数2表示调用委托时传递的参数
Console.WriteLine(r ); //print 200 Loop ( )
//创建一个LoopExpression表示循环语句表达式
//示例:
var label = Expression.Label ( typeof ( int ) );
var x = Expression.Variable ( typeof ( int ) , "x" );
var block = Expression.Block (
    new [ ] { x } ,
    Expression.Assign ( x , Expression.Constant ( 0 ) ) ,
    Expression.Loop (
        Expression.IfThenElse (
            Expression.LessThan (
                x ,
                Expression.Constant ( 10 )
            ) ,
            Expression.PostIncrementAssign ( x ) ,// x++
            Expression.Break ( label , x ) //将x作为标签的值
        ) ,
    label
    )
);
int r = Expression.Lambda<Func<int>> ( block ).Compile ( ) ( );
Console.Write(r ); // print 10 PropertyOrField ( )
//创建一个MemberExpression表示访问成员字段或属性的表达式,类似的有Field( )、Property( )方法
//示例:
Animal horse = new Animal { Name = "sam" };
MemberExpression member = Expression.PropertyOrField ( Expression.Constant ( horse ) , "Name" );
Console.WriteLine(member.ToString()); 
string Name = Expression.Lambda<Func<string>> ( member ).Compile ( ) ( );
Console.WriteLine(Name ); // print sam Invoke ( )
//创建一个InvocationExpression表示执行Lambda并传递实参的表达式
//示例:
Expression<Func<int , int , bool>> InvocationInfo = ( i , ii ) => ( i + ii ) > 1;
InvocationExpression invocationExpression = Expression.Invoke (
    InvocationInfo , //函数
    Expression.Constant ( 1 ) , //参数
    Expression.Constant ( 2 )  //参数
); Console.WriteLine(invocationExpression.ToString ( ) ); // Invoke ( (i, ii) => ( (i + ii) > 3 ), 1, 2 ) 
bool r = Expression.Lambda<Func<bool>> ( invocationExpression ).Compile ( ) ( );
Console.WriteLine(r ); // print true ElementInit ( MethodInfo addMethod , Expression expr )
//创建一个ElementInit表示调用集合的Add方法添加元素
//示例:参看下面的ListInit()方法 New ( )
//创建一个NewExpression表示调用无参构造函数
//示例:参看下面的ListInit()方法 ListInit ( )
//创建一个ListInitExpression表示集合初始化器
//示例:
string proner1 = "sam";
string proner2 = "korn"; MethodInfo add = typeof ( List<string> ).GetMethod ( "Add" );
//生成表达式:Add("sam")
var elm1 = Expression.ElementInit (
    add ,
    Expression.Constant ( proner1 )
);
//生成表达式:Add("korn")
var elm2 = Expression.ElementInit (
    add ,
    Expression.Constant ( proner2 )
);
//生成表达式:new List<string>( )
var list = Expression.New ( typeof ( List<string> ) );
//生成表达式:new List<string> { "sam" , "korn" }
var listObject = Expression.ListInit (
    list ,
    elm1 ,
    elm2
); Console.WriteLine(listObject.ToString() ); Quote ( )
//将LambdaExpression包装为一个常量
//示例:
var add = Expression.Add ( Expression.Constant ( 1 ) , Expression.Constant ( 1 ) );
var expr1 = Expression.Lambda<Func<int>> ( add );
var expr2 = Expression.Quote ( Expression.Lambda ( add ) );
Console.WriteLine(expr1.Compile ( ) ( ) ); //print 2
Console.WriteLine(expr2 ); // print  ()=>1+1

经过以上的逐步分析,现在可以创建一个稍微复杂一点的例子了,下面演示了创建一个块语句表达式,在块中创建了双重循环表达式,通过调用Lambda方法提取一个委托来执行表达式树:


LabelTarget outerBreak = Expression.Label ( );
LabelTarget innerBreak = Expression.Label ( );
var x = Expression.Variable ( typeof ( int ) , "x" );
var y = Expression.Variable ( typeof ( int ) , "y" );
var result = Expression.Variable ( typeof ( int ) , "result" ); var block = Expression.Block (
    new [ ] { x } ,
    Expression.Assign ( x , Expression.Constant ( 1 ) ) ,
    //循环
    Expression.Loop (
        //条件判断
        Expression.IfThenElse (
            //如果表达式为真
            Expression.LessThan ( x , Expression.Constant ( 10 ) ) , // if x<10
            //为真时执行
            Expression.Block (
                new [ ] { y } ,
                Expression.Assign ( y , Expression.Constant ( 1 ) ) ,
                //内层循环
                Expression.Loop (
                    Expression.IfThenElse (
                        Expression.LessThanOrEqual ( y , x ) , // if y <= x
                        //为真时执行
                        Expression.Block (
                            new [ ] { result } ,
                            Expression.Assign ( result , Expression.Multiply ( x , y ) ) ,
                            Expression.Call ( null , typeof ( Console ).GetMethod ( "Write" , new Type [ ] { typeof ( int ) } ) , y ) ,
                            Expression.Call ( null , typeof ( Console ).GetMethod ( "Write" , new Type [ ] { typeof ( string ) } ) , Expression.Constant ( "×" ) ) ,
                            Expression.Call ( null , typeof ( Console ).GetMethod ( "Write" , new Type [ ] { typeof ( int ) } ) , x ) ,
                            Expression.Call ( null , typeof ( Console ).GetMethod ( "Write" , new Type [ ] { typeof ( string ) } ) , Expression.Constant ( "=" ) ) ,
                            Expression.Call ( null , typeof ( Console ).GetMethod ( "Write" , new Type [ ] { typeof ( int ) } ) , result ) ,
                            Expression.Call ( null , typeof ( Console ).GetMethod ( "Write" , new Type [ ] { typeof ( string ) } ) , Expression.Constant ( "\t" ) ) ,
                            Expression.PostIncrementAssign ( y ) // y++
                        ) ,
                        //为假时退出内层循环
                        Expression.Break ( innerBreak )
                    ) ,
                    innerBreak
                ) ,//内层循环end
                Expression.Call ( null , typeof ( Console ).GetMethod ( "WriteLine" , new Type [ ] { typeof ( string ) } ) , Expression.Constant ( "" ) ) ,
                Expression.PostIncrementAssign ( x ) // x++
            ) ,
            //为假时执行
            Expression.Break ( outerBreak )
            )
    , outerBreak )
); Expression.Lambda<Action> ( block ).Compile ( ) ( );

表达式树(Expression Tree)

Linq查询中的表达式树

为什么有时候Linq查询需要表达式树?

Linq To SQL

Queryable类为IQueryable<T>实现了一系列的扩展方法用于Linq To SQL的查询,这些扩展方法大部分都返回一个IQueryable<T>类型,IQueryable <T>有一个Expression类型的属性叫做Expression,当使用Linq To SQL执行Linq查询时会调用那些扩展方法,而扩展方法都要求一个Expression<T>类型的参数,扩展方法接收这个作为参数的表达式树后将它作为IQueryable <T>集合的Expression属性来使用,IQueryable <T>还有一个叫做IQueryProvider(Linq查询提供程序)的属性,IQueryProvider类拿到Expression表达式树后会对其进行解析以便生成纯SQL语句,再将SQL字符串发送到数据库以便执行。所以从此处可以看出来,表达式树具有很高的灵活性,它并非可执行的C#代码,但通过Expression提供的一系列属性和方法,使用ExpressionVisitor(对表达式树进行访问和修改的类)你可以对其进行解析,根据需求的不同从而动态修改表达式树中的表达式以便生成可在非C#环境执行的代码。

Linq To Object

Enumerable类为IEnumerable<T>实现了一系列的扩展方法用于Linq To Object的查询,这些扩展方法大部分都返回一个IEnumerable<T>类型,IEnumerable<T>没有需要接收表达式树作为参数的扩展方法,因为Linq To Object是直接查询内存中的数据,不需要转化为SQL语句,所以根本用不上表达式树。

https://www.cnblogs.com/myrocknroll/p/7630080.html

表达式树(Expression Tree)的更多相关文章

  1. 表达式树&lpar;Expression Tree&rpar;

    饮水思源 本文并非原创而是下面网址的一个学习笔记 https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/e ...

  2. &lbrack;C&num;&rsqb; C&num; 知识回顾 - 表达式树 Expression Trees

    C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...

  3. 表达式树 Expression

    转载泛型方法动态生成表达式树 Expression public string GetGridJSON(TraderInfo model) { IQueryable<TraderInfo> ...

  4. C&num; 知识回顾 - 表达式树 Expression Trees

    C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...

  5. C&num; 表达式树 Expression

    表达式树是定义代码的数据结构. 它们基于编译器用于分析代码和生成已编译输出的相同结构. 几种常见的表达式 BinaryExpression 包含二元运算符的表达式 BinaryExpression b ...

  6. 泛型方法动态生成表达式树 Expression

    public string GetGridJSON(TraderInfo model) { IQueryable<TraderInfo> Temp = db.TraderInfo; if ...

  7. jQuery find&lpar;&rpar; 搜索所有段落中的后代 C&num; find&lpar;&rpar; 第一个匹配元素 Func 有返回值 Action是没有返回值 Predicate 只有一个参数且返回值为bool 表达式树Expression

    所有p后代span Id为 TotalProject 的 select 标签 的后代 option标签 为选中的 text using System; using System.Collections ...

  8. 利用表达式树Expression优化反射性能

    最近做了一个.Net Core环境下,基于NPOI的Excel导入导出以及Word操作的服务封装,涉及到大量反射操作,在性能优化过程中使用到了表达式树,记录一下. Excel导入是相对比较麻烦的一块, ...

  9. 表达式树Expression

    Expression表达式树动态查询 在进行数据列表的查询中,我们通常会使用两种方式进行查询: linq查询 数据库sql语句查询 这样固然可以实现查询,本人之前也都是这么做的,因为查询的条件很少.使 ...

  10. Func委托与表达式树Expression

    最近在写ORM框架,其中遇到一个难点,就是作为框架调用方如何将查询条件传入框架内.其中就用到了Expression. Func委托 要Expression先要了解Func委托,Func委托的样式是: ...

随机推荐

  1. LINQ to SQL语句&lpar;6&rpar;之Group By&sol;Having

    适用场景:分组数据,为我们查找数据缩小范围. 说明:分配并返回对传入参数进行分组操作后的可枚举对象.分组:延迟 1.简单形式: var q = from p in db.Products group ...

  2. Mysql学习笔记(四)聊聊数据库索引

    小心情(可直接跳到分割线后) 今天心情好些了.一些浓的化不开的坏情绪,也渐渐的在晚上解决掉一个复杂的逻辑问题后,渐渐消散了. 今天中午去吃饭的时候,坤哥漫不经心的说:'我这么多年终于悟出了一个道理,人 ...

  3. Linq To Sql 语法 子查询 &amp&semi; In &amp&semi; Join

    子查询 描述:查询订单数超过5的顾客信息 查询句法: var 子查询 =from cin ctx.Customers                    where                  ...

  4. 用通俗的例子解释OAuth和OpenID的区别【原】

    什么是OAuth(Wiki) 什么是OpenID(Wiki) 详细的定义可以看wiki,下面举个例子说说我的理解 现在很多网站都可以用第三方的账号登陆,比如,现在我要登录淘宝买东西,而如果我没有淘宝的 ...

  5. MySQL linux二进制安装

    200 ? "200px" : this.width)!important;} --> 介绍 1.创建用户和目录 groupadd mysql useradd -r -g m ...

  6. JavaScript函数的各种调用模式

    函数是JavaScript世界里的第一公民,换句话来说,就是我们如果可以精通JavaScript函数的使用,那么对JavaScript的运用可以更游刃有余了.熟悉JavaScript的人应该都知道,同 ...

  7. C3P0 APPARENT DEADLOCK

    一,c3p0执行一段时间后报错例如以下 W 07-26_00:58:27 ThreadPoolAsynchronousRunner.java 608 com.mchange.v2.async.Thre ...

  8. SpringBoot SpEL表达式注入漏洞-分析与复现

    目录 0x00前言 0x01触发原因 0x02调试分析 0x03补丁分析 0x04参考文章 影响版本: 1.1.0-1.1.12 1.2.0-1.2.7 1.3.0 修复方案:升至1.3.1或以上版本 ...

  9. CentOS7&period;5下安装Python3&period;7 --python3

    1.将本地安装包上传到远程主机上 scp Python-3.7.0.tgz root@123.206.74.24:/root 2.扩展 安装Python之前安装Python相关的依赖包(主要是u红色部 ...

  10. pl&sol;sql快速输入select等语句

    平时对数据库操作的时候,输入DML语句,很浪费时间,我们想要这样的效果 ,输入sf,plsql就会自动输入select * from.我们需要在plsql中进行如下设置即可: 工具(tools)--& ...