加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

表达式树(Expression Tree)

发布时间:2020-12-14 04:20:01 所属栏目:大数据 来源:网络整理
导读:你每创建一个表示表达式的实例时,都可以将该类型实例看成是一棵表达式树。每种表示表达式的类型都有一个具体的类型,如Expression的Variable()方法创建的是ParameterExpression类型的表达式,Expression的Add()方法创建的则是BinaryExpression类型的表达式

你每创建一个表示表达式的实例时,都可以将该类型实例看成是一棵表达式树。每种表示表达式的类型都有一个具体的类型,如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>>?(?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属性,此属性是一个枚举数,用以获取表达式属于什么种类(根据其行为判定)。

? /*节点的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?,?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?,?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?,?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?,?y?)?=>?x?!=?y?&&?x?!=?0;
Console.WriteLine(expressionInfo.Type?);?//print?System.Func`2[System.Int32,System.Int32]

Compile?(?)
//将LambdaExpression封装的表达式生成委托返回
//示例:
Expression<Func<int?,?y?)?=>?x?!=?y?&&?x?!=?0;
Func<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?)?,?Expression.Constant?(?1?)?).ToString?(?);?//x+1
Console.WriteLine(s?);?//x+1
var?s2?=?Expression.AddAssign?(?Expression.Parameter?(?typeof?(?int?)?,?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?,?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?,
????Expression.Assign?(?x?,?Expression.Constant?(?100000?)?)?,
????Expression.Assign?(?y?,
????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?)?,
????Expression.IfThenElse?(
????????Expression.GreaterThanOrEqual?(?x?,?y?)?,?//?if?(?x?>=?y?)
????????Expression.Call?(?null?,?typeof?(?Console?).GetMethod?(?"WriteLine"?,?Expression.Constant?(?"x>y==true"?)?)?,?//条件为真时执行
????????Expression.Call?(?null?,?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.Return?(?label?,?x?)?//直接跳转到与label标签关联的LabelExpression表达式
?????????)
?????)?,
?????//如果x==2
?????Expression.IfThen?(
?????????Expression.Equal?(?x?,?Expression.Constant?(?2?)?)?,
?????//与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.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?,?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.Loop?(
????????//条件判断
????????Expression.IfThenElse?(
????????????//如果表达式为真
????????????Expression.LessThan?(?x?,?Expression.Constant?(?10?)?)?,?//?if?x<10
????????????//为真时执行
????????????Expression.Block?(
????????????????new?[?]?{?y?}?,
????????????????Expression.Assign?(?y?,
????????????????//内层循环
????????????????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?)?}?)?,?Expression.Constant?(?"×"?)?)?,?Expression.Constant?(?"="?)?)?,?result?)?,?Expression.Constant?(?"t"?)?)?,
????????????????????????????Expression.PostIncrementAssign?(?y?)?//?y++
????????????????????????)?,
????????????????????????//为假时退出内层循环
????????????????????????Expression.Break?(?innerBreak?)
????????????????????)?,
????????????????????innerBreak
????????????????)?,//内层循环end
????????????????Expression.Call?(?null?,?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语句,所以根本用不上表达式树。

?

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读