zoukankan      html  css  js  c++  java
  • C#

    表达式树(Expression Tree)

    表达式树是不可执行的代码,它只是用于表示一种树状的数据结构,树上的每一个节点都表示为某种表达式类型,大概有25种表达式类型,它们都派生自Expression类。创建表达式树具体有两个优势:

    1.对表达式树的代码进行编辑修改,使表达式树中的代码变成动态代码,根据不同的数据库修改树上的代码逻辑从而达到动态切换数据库查询语句的目的,用表达式树可以动态构建针对不同数据库的查询语句。

    2.完成类似反射访问未知对象的属性,通过动态构造表达式树,生成委托。 

     

    三种方式创建表达式树

    Expression(表达式类)

    此类可以称为表示表达式的类型,或也称为一棵表达式树。因为当你每创建一个表示表达式的实例时,都可以将该类型实例看成是一棵表达式树。每种表示表达式的类型都有一个具体的类型,如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<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的子类

    树的每个子节点是一个具体的表达式,它们都有自己的表达式类型,这些类型从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属性,此属性是一个枚举数,用以获取表达式属于什么种类(根据其行为判定)。

    Add
    //加法运算,如 a + b, ,不进行溢出检查,针对数值操作数。
    AddAssign
    //加法复合赋值运算,如 ( a += b), ,不进行溢出检查,针对数值操作数。
    AddAssignChecked
    //加法复合赋值运算,如 ( a += b), ,进行溢出检查,针对数值操作数。
    AddChecked
    //加法运算,如 ( a + b), ,进行溢出检查,针对数值操作数。
    And
    //按位或逻辑 AND 操作,如 ( a & b) 在 C# 和 (a And b) 在 Visual Basic 中。
    AndAlso
    //在条件 AND 仅当第一个操作数的计算结果为才计算第二个操作数的操作 true。 它对应于 ( a && b) 在 C# 和 (a AndAlso b) 在 Visual Basic 中。
    AndAssign
    //按位或逻辑 AND 复合赋值运算,如 ( a &= b) C# 中。
    ArrayIndex
    //索引操作在一维数组中,如 array [ index ] 在 C# 或 array(index) 在 Visual Basic 中。
    ArrayLength
    //获取一维数组的长度,如操作 array.Length。
    Assign
    //赋值运算,如 (a = b )。
    Block
    //表达式的块。
    Call
    //某个方法调用,如在 obj.sampleMethod ( ) 表达式。
    Coalesce
    //一个表示空合并操作,如节点 ( a ?? b) 在 C# 或 If(a, b) 在 Visual Basic 中。
    Conditional
    //条件运算,如 a > b? a : b 在 C# 或 If(a > b, a, b) 在 Visual Basic 中。
    Constant
    //常量的值。
    Convert
    //强制转换或转换操作中,如 ( SampleType)obj C# 中或 CType(obj, SampleType) 在 Visual Basic 中。 对于数值的转换,如果转换后的值对于目标类型来说太大不引发异常。
    ConvertChecked
    //强制转换或转换操作中,如 ( SampleType)obj C# 中或 CType(obj, SampleType) 在 Visual Basic 中。 对于数值的转换,如果转换后的值不符合目标类型是引发异常。
    DebugInfo
    //调试信息。
    Decrement
    //一元递减操作,如 ( a - 1) C# 和 Visual Basic 中。 该对象 a 不应就地修改。
    Default
    //默认值。
    Divide
    //除法运算,如 ( a / b), ,针对数值操作数。
    DivideAssign
    //除的复合赋值运算,如 ( a /= b), ,针对数值操作数。
    Dynamic
    //动态操作。
    Equal
    //一个表示相等比较,如节点 ( a == b) 在 C# 或 (a = b) 在 Visual Basic 中。
    ExclusiveOr
    //按位或逻辑 XOR 操作,如 ( a ^ b) 在 C# 或 (a Xor b) 在 Visual Basic 中。
    ExclusiveOrAssign
    //按位或逻辑 XOR 复合赋值运算,如 ( a ^= b) C# 中。
    Extension
    //扩展表达式。
    Goto
    //一个"转到"表达式,如 goto Label 在 C# 或 GoTo Label 在 Visual Basic 中。
    GreaterThan
    //"大于"比较,如 ( a > b)。
    GreaterThanOrEqual
    //"大于或等于"比较,如 ( a >= b)。
    Increment
    //一元递增操作,如 ( a + 1) C# 和 Visual Basic 中。 该对象 a 不应就地修改。
    Index
    //索引操作或访问不采用参数的属性的操作。
    Invoke
    //操作调用的委托或 lambda 表达式,如 sampleDelegate.Invoke ( )。
    IsFalse
    //一个 false 条件值。
    IsTrue
    //一个 true 条件值。
    Label
    //标签。
    Lambda
    //Lambda 表达式,如 a => a + a 在 C# 或 Function(a) a + a 在 Visual Basic 中。
    LeftShift
    //按位左移运算,如 ( a << b)。
    LeftShiftAssign
    //按位左移复合赋值运算,如 ( a <<= b)。
    LessThan
    //"小于"比较,如 ( a<b)。
    LessThanOrEqual
    //"小于或等于"比较,如 ( a <= b)。
    ListInit
    //创建一个新的操作的 IEnumerable 对象,并对其进行初始化从列表中的元素,如 new List<SampleType>(){ a, b, c } 在 C# 或 Dim sampleList = { a, b, c } 在 Visual Basic 中。
    Loop
    //一个循环,如 for 或 while。
    MemberAccess
    //从一个字段或属性,如读取操作 obj.SampleProperty。
    MemberInit
    //运算,创建一个新的对象并初始化一个或多个成员,如 new Point { X = 1, Y = 2 } 在 C# 或 New Point With {.X = 1, .Y = 2} 在 Visual Basic 中。
    Modulo
    //算术余数运算,如 ( a % b) 在 C# 或 (a Mod b) 在 Visual Basic 中。
    ModuloAssign
    //算术余数复合赋值运算,如 ( a %= b) C# 中。
    Multiply
    //乘法运算,如 ( a* b ), ,不进行溢出检查,针对数值操作数。
    MultiplyAssign
    //乘法复合赋值运算,如 ( a *= b), ,不进行溢出检查,针对数值操作数。
    MultiplyAssignChecked
    //乘法复合赋值运算,如 ( a *= b), ,,进行溢出检查,针对数值操作数。
    MultiplyChecked
    //乘法运算,如 ( a* b ), ,,进行溢出检查,针对数值操作数。
    Negate
    //算术求反运算,如 (-a)。 该对象 a 不应就地修改。
    NegateChecked
    //算术求反运算,如 (-a), ,,进行溢出检查。 该对象 a 不应就地修改。
    New
    //调用构造函数以创建新的对象,如操作 new SampleType()。
    NewArrayBounds
    //创建一个新数组,其中每个维度的下限指定,如操作 new SampleType[dim1, dim2] 在 C# 或 New SampleType(dim1, dim2) 在 Visual Basic 中。
    NewArrayInit
    //操作,创建一个新的一维数组并对其进行初始化从列表中的元素,如 new SampleType[]{a, b, c} 在 C# 或 New SampleType(){a, b, c} 在 Visual Basic 中。
    Not
    //按位求补或逻辑求反运算。 在 C# 中,则等同于 (~a) 整型和 (!a) 布尔值。 在 Visual Basic 中,则等同于 (Not a)。 该对象 a 不应就地修改。
    NotEqual
    //不相等比较,如 (a != b) 在 C# 或 (a <> b) 在 Visual Basic 中。
    OnesComplement
    //一个二进制反码运算,如 (~a) C# 中。
    Or
    //按位或逻辑 OR 操作,如 (a | b) 在 C# 或 (a Or b) 在 Visual Basic 中。
    OrAssign
    //按位或逻辑 OR 复合赋值运算,如 (a |= b) C# 中。
    OrElse
    //短路条件 OR 操作,如 (a || b) 在 C# 或 (a OrElse b) 在 Visual Basic 中。
    Parameter
    //对参数或变量的表达式的上下文中定义的引用。 有关更多信息,请参见ParameterExpression。
    PostDecrementAssign
    //一元后缀递减,如 (a--)。 该对象 a 应就地修改。
    PostIncrementAssign
    //一元后缀递增,如 (a++)。 该对象 a 应就地修改。
    Power
    //如引发数字进行幂运算的数学运算 (a ^ b) 在 Visual Basic 中。
    PowerAssign
    //如引发数字进行幂运算的复合赋值运算 (a ^= b) 在 Visual Basic 中。
    PreDecrementAssign
    //一元前缀递减,如 (--a)。 该对象 a 应就地修改。
    PreIncrementAssign
    //一元前缀递增,如 (++a)。 该对象 a 应就地修改。
    Quote
    //具有类型的常量值的表达式 Expression。 一个 Quote 节点可以包含对它所代表的表达式的上下文中定义的参数的引用。
    RightShift
    //按位右移运算,如 (a >> b)。
    RightShiftAssign
    //按位右移复合赋值运算,如 (a >>= b)。
    RuntimeVariables
    //运行时变量的列表。 有关详细信息,请参阅RuntimeVariablesExpression。
    Subtract
    //减法运算,如 (a - b), ,不进行溢出检查,针对数值操作数。
    SubtractAssign
    //减法复合赋值运算,如 (a -= b), ,不进行溢出检查,针对数值操作数。
    SubtractAssignChecked
    //减法复合赋值运算,如 (a -= b), ,,进行溢出检查,针对数值操作数。
    SubtractChecked
    //算术减法运算,如 (a - b), ,,进行溢出检查,针对数值操作数。
    Switch
    //一个切换操作,如 switch 在 C# 或 Select Case 在 Visual Basic 中。
    Throw
    //引发异常,如操作 throw new Exception()。
    Try
    //一个 try-catch 表达式。
    TypeAs
    //显式引用或装箱转换在其中 null 如果转换失败,如提供 (obj as SampleType) 在 C# 或 TryCast(obj, SampleType) 在 Visual Basic 中。
    TypeEqual
    //确切类型测试。
    TypeIs
    //一种类型测试,如 obj is SampleType 在 C# 或 TypeOf obj is SampleType 在 Visual Basic 中。
    UnaryPlus
    //一元正运算,如 (+a)。 预定义的一元正运算的结果是操作数的值,但用户定义的实现可能有不寻常的结果。
    Unbox
    //取消装箱值类型的操作,如 unbox 和 unbox.any MSIL 中的说明。
    /*节点的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 ( " " ) ) ,
                                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 ( ) ( );

     

    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语句,所以根本用不上表达式树。

     

    C# - 学习总目录

    参考:http://www.infoq.com/cn/articles/dot-net-expression-tree

  • 相关阅读:
    智能移动机器人背后蕴含的技术——激光雷达
    Kalman Filters
    Fiddler抓HttpClient的包
    VSCode开发WebApi EFCore的坑
    WPF之小米Logo超圆角的实现
    windows react打包发布
    jenkins in docker踩坑汇总
    Using ML.NET in Jupyter notebooks 在jupyter notebook中使用ML.NET ——No design time or full build available
    【Linux知识点】CentOS7 更换阿里云源
    【Golang 报错】exec gcc executable file not found in %PATH%
  • 原文地址:https://www.cnblogs.com/myrocknroll/p/7630080.html
Copyright © 2011-2022 走看看