C#进阶之路(六):表达式进⾏类的赋值
好久没更新这个系列了,最近看.NET CORE源码的时候,发现他的依赖注⼊模块的很多地⽅⽤了表达式拼接实现的。⽐如如下代码
private Expression<Func<ServiceProviderEngineScope, object>> BuildExpression(IServiceCallSite callSite)
{
var context = new CallSiteExpressionBuilderContext
{
ScopeParameter = ScopeParameter
};
var serviceExpression = VisitCallSite(callSite, context);
if (context.RequiresResolvedServices)
{
return Expression.Lambda<Func<ServiceProviderEngineScope, object>>(
Expression.Block(
new [] { ResolvedServices },
ResolvedServicesVariableAssignment,
Lock(serviceExpression, ResolvedServices)),
ScopeParameter);
}
return Expression.Lambda<Func<ServiceProviderEngineScope, object>>(serviceExpression, ScopeParameter);
}
所以今天我们先⼀起了解下表达式树以及它的⼀种实⽤应⽤——表达式树进⾏类的快速赋值。
提⽰:学习这⼀章,需要有⼀定拉姆达基础,如果不太了解拉姆达,推荐阅读。
⼀、初识表达式树
表达式树是将我们原来可以直接由代码编写的逻辑以表达式的⽅式存储在树状的结构⾥,从⽽可以在运⾏时去解析这个树,然后执⾏,实现动态的编辑和执⾏代码。LINQ to SQL就是通过把表达式树翻译成SQL来实现的,所以了解表达树有助于我们更好的理解 LINQ to SQL,同时如果你有兴趣,可以⽤它创造出很多有意思的东西来。
根据Lambda表达式来创建表达式树,这应该是最直接的创建表达式树的⽅式了。
Expression<Func<int, int>> expr = x => x + 1;
Console.WriteLine(expr.ToString()); // x=> (x + 1)
// 下⾯的代码编译不通过
Expression<Func<int, int, int>> expr2 = (x, y) => { return x + y; };
Expression<Action<int>> expr3 = x => { };
这种⽅式只能创建最简单的表达式树,复杂点的编译器就不认识了。
右边是⼀个Lambda表达式,⽽左边是⼀个表达式树。为什么可以直接赋值呢?这个就要多亏我们的Expression<TDelegate>泛型类了。⽽Expression<TDelegate>是直接继承⾃LambdaExpression的,我们来看⼀下Expression的构造函数:
internal Expression(Expression body, string name, bool tailCall,ReadOnlyCollection<ParameterExpression> parameters)
: base(typeof(TDelegate), name, body, tailCall, parameters)
{
}
实际上这个构造函数什么也没有做,只是把相关的参数传给了⽗类,也就是LambdaExpression,由它把我们表达式的主体,名称,以及参数保存着。
Expression<Func<int, int>> expr = x => x + 1;
Console.WriteLine(expr.ToString()); // x=> (x + 1)
var lambdaExpr = expr as LambdaExpression;
Console.WriteLine(lambdaExpr.Body); // (x + 1)
Console.WriteLine(lambdaExpr.ReturnType.ToString()); // System.Int32
foreach (var parameter in lambdaExpr.Parameters)
{
Console.WriteLine("Name:{0}, Type:{1}, ",parameter.Name,parameter.Type.ToString());
}
//Name:x, Type:System.Int32
⼆、创建⼀个复杂的La mbda表达式树
m bda
上⾯我们讲到直接由Lambda表达式的⽅式来创建表达式树,可惜只限于⼀种类型。下⾯我们就来演⽰⼀下如何创建⼀个⽆参⽆返回值的表达式树。
// 下⾯的⽅法编译不能过
/*
Expression<Action> lambdaExpression2 = () =>
{
for (int i = 1; i <= 10; i++)
{
Console.WriteLine("Hello");
}
};
*/
// 创建 loop表达式体来包含我们想要执⾏的代码
LoopExpression loop = Expression.Loop(
Expression.Call(
null,
typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),
Expression.Constant("Hello"))
);
// 创建⼀个代码块表达式包含我们上⾯创建的loop表达式
BlockExpression block = Expression.Block(loop);
// 将我们上⾯的代码块表达式
Expression<Action> lambdaExpression = Expression.Lambda<Action>(block);
lambdaExpression.Compile().Invoke();
上⾯我们通过⼿动编码的⽅式创建了⼀个⽆参的Action,执⾏了⼀组循环。代码很简单,重要的是我们要熟悉这些各种类型的表达式以及他们的使⽤⽅式。上⾯我们引⼊了以下类型的表达式:
看起来神密的表达式树也不过如此嘛?如果⼤家去执⾏上⾯的代码,就会陷⼊死循环,我没有为loop加⼊break的条件。为了⽅便⼤家理解,我是真的⼀步⼀步来啊,现在我们就来终⽌这个循环。就像上⾯那⼀段不能编译通过的代码实现的功能⼀样,我们要输出10
个”Hello”。
上⾯我们先写了⼀个LoopExpression,然后把它传给了BlockExpresson,从⽽形成的的⼀块代码或者我们也可以说⼀个⽅法体。但是如果我们有多个执⾏块,⽽且这多个执⾏块⾥⾯需要处理同⼀个参数,我们就得在block⾥⾯声明这些参数了。
ParameterExpression number=Expression.Parameter(typeof(int),"number");
BlockExpression myBlock = Expression.Block(
new[] { number },
Expression.Assign(number, Expression.Constant(2)),
Expression.AddAssign(number, Expression.Constant(6)),
Expression.DivideAssign(number, Expression.Constant(2)));
Expression<Func<int>> myAction = Expression.Lambda<Func<int>>(myBlock);
Console.WriteLine(myAction.Compile()());
// 4
我们声明了⼀个int的变量并赋值为2,然后加上6最后除以2。如果我们要⽤变量,就必须在block的你外⾯声明它,并且在block⾥⾯把它引⼊进来。否则在该表达式树时会出现,变量不在作⽤域⾥的错。
下⾯我们继续我们未完成的⼯作,为循环加⼊退出条件。为了让⼤家快速的理解loop的退出机制,我们先来看⼀段伪代码:
LabelTarget labelBreak = Expression.Label();
Expression.Loop(
"如果条件成功"
"执⾏成功的代码"
"否则"
Expression.Break(labelBreak) //跳出循环
, labelBreak);
我们需要借助于LabelTarget 以及Expression.Break来达到退出循环的⽬地。下⾯我们来看⼀下真实的代码:
LabelTarget labelBreak = Expression.Label();
ParameterExpression loopIndex = Expression.Parameter(typeof(int), "index");
BlockExpression block = Expression.Block(
new[] { loopIndex },
// 初始化loopIndex =1
Expression.Assign(loopIndex, Expression.Constant(1)),
Expression.Loop(
Expression.IfThenElse(
// if 的判断逻辑
Expression.LessThanOrEqual(loopIndex, Expression.Constant(10)),
json值的类型有哪些// 判断逻辑通过的代码
Expression.Block(
Expression.Call(
null,
typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),
Expression.Constant("Hello")),
Expression.PostIncrementAssign(loopIndex)),
// 判断不通过的代码
Expression.Break(labelBreak)
),labelBreak));
// 将我们上⾯的代码块表达式
Expression<Action> lambdaExpression = Expression.Lambda<Action>(block);
lambdaExpression.Compile().Invoke();
好吧,我们⼜学了⼏个新的类型的表达式,来总结⼀下:
到这⾥,我想⼤家应该对表达式树的构建有了⼀个清楚的认识。⾄于为什么不允许我们直接基于复杂的Lambda表达式来创建表达式树呢?这⾥的Lambda表达式实际上是⼀个Expression Body。
这个Expression Body实际上就是我们上⾯讲到的Expression中的⼀种。
也就是说编译器需要时间去分析你到底是哪⼀种?
最简单的x=> x+1之类的也就是Func<TValue,TKey> 是很容易分析的。
实际这⾥⾯允许的Expression Body只有BinaryExpression。
最后,我们来完整的看⼀下.NET都为我们提供了哪些类型的表达式(下⾯这些类都是继承⾃Expression)。
表达式类型
三、类的赋值
在代码中经常会遇到需要把对象复制⼀遍,或者把属性名相同的值复制⼀遍。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论