【转】由浅入深表达式树(一)制造表达式bwin亚洲必赢5566手机版

     
 为啥要学习表明式树?表明式树是将我们本来可以一贯由代码编写的逻辑以表达式的情势存储在树状的布局里,从而可以在运作时去分析这多少个树,然后实施,实现动态的编制和实施代码。LINQ
to
SQL就是通过把表明式树翻译成SQL来实现的,所以了然表达树有助于我们更好的知道
LINQ to SQL,同时尽管你有趣味,可以用它创设出成千上万妙趣横生的事物来。

  表明式树是随着.NET
3.5生产的,所以现在也不算什么新技巧了。可是不清楚有些人是对它知道的很透彻,
在上一篇兰姆(Lamb)da表达式的回升中就看的出豪门对Lambda表明式和表明式树如故相比感兴趣的,这大家就来可以的看一看那几个培训了LINQ
to SQL以及让LINQ to 伊夫(Eve)rything的好东西呢。

  本类别计划三篇,第一篇紧要介绍表达式树的始建格局。第二篇首要介绍表明式树的遍历问题。第三篇,将使用表达式树打造一个协调的LinqProvider。

  本文首要内容:

创设一个概括的Lambda表明式树

  在
上一篇Lambda表达式中大家提到了足以一向依照兰姆da表明式来成立表明式树,那应当是最直白的始建表明式树的不二法门了。

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 => {  };

  可是别想象的太美好,这种方法只好创制最简便的表明式树,复杂点的编译器就不认识了。

  右侧是一个兰姆(Lamb)da表达式,而右边是一个表明式树。为啥可以一向赋值呢?那些即将多亏我们的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

  bwin亚洲必赢5566手机版 1

  简单的来说,Expression<TDelegate>泛型类做了一层封装,方便我们依照Lambda表明式来创立兰姆(Lamb)da表明式树。它们中间有一个变换过程,而以此转换的经过就生出在我们编译的时候。还记得大家Lambda表明式中讲的么?Lambda表达式在编译之后是司空眼惯的方法,而兰姆(Lamb)da式树是以一种树的构造被加载到我们的运行时的,只有这么我们才足以在运行时去遍历那个树。可是怎么我们无法依照Expression<TDelegate>来创制相比较复杂的表明式树啊?您请接着往下看。

创办一个错综复杂的兰姆da表明式树

  下边我们就来一步一步的创始一个错综复杂的表明式树,你们准备好了么?下边我们讲到直接由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,执行了一组循环。代码很简单,重要的是大家要熟练这一个各样类型的表明式以及她们的采取办法。下边我们引入了以下序列的表明式:

bwin亚洲必赢5566手机版 2

  看起来神密的表明式树也不过这样嘛?倘若我们去实践上边的代码,就会深陷死循环,我尚未为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)),
            // 判断逻辑通过的代码
            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();

  bwin亚洲必赢5566手机版 3

  希望下面的代码没有挡住你学习表明式树的厉害J 。

  好啊,大家又学了多少个新的花色的表明式,来总结一下:

bwin亚洲必赢5566手机版 4

  到此地,我想我们应该对发挥式树的构建有了一个清楚的认识。至于为啥不容许大家一贯基于复杂的Lambda表明式来创立表明式树啊?

  • 此处的兰姆da表达式实际上是一个Expression Body。
  • 其一Expression Body实际上就是我们地点讲到的Expression中的一种。
  • 也就是说编译器需要时日去分析你到底是哪类?
  • 最简单易行的x=> x+1之类的也就是Func<TValue,TKey>
    是很容易分析的。
  • 骨子里这其间允许的Expression Body只有BinaryExpression。

bwin亚洲必赢5566手机版 5

  最后,大家来全体的看一下.NET都为我们提供了哪些类型的表明式(下边这一个类都是继续自Expression)。

bwin亚洲必赢5566手机版 6

TypeBinaryExpression

TypeBinaryExpression typeBinaryExpression =
    Expression.TypeIs(
        Expression.Constant("spruce"),
        typeof(int));

Console.WriteLine(typeBinaryExpression.ToString());
// ("spruce" Is Int32)

IndexExpression

ParameterExpression arrayExpr = Expression.Parameter(typeof(int[]), "Array");

ParameterExpression indexExpr = Expression.Parameter(typeof(int), "Index");

ParameterExpression valueExpr = Expression.Parameter(typeof(int), "Value");

Expression arrayAccessExpr = Expression.ArrayAccess(
    arrayExpr,
    indexExpr
);

Expression<Func<int[], int, int, int>> lambdaExpr = Expression.Lambda<Func<int[], int, int, int>>(
        Expression.Assign(arrayAccessExpr, Expression.Add(arrayAccessExpr, valueExpr)),
        arrayExpr,
        indexExpr,
        valueExpr
    );

Console.WriteLine(arrayAccessExpr.ToString());
// Array[Index]

Console.WriteLine(lambdaExpr.ToString());
// (Array, Index, Value) => (Array[Index] = (Array[Index] + Value)) 

Console.WriteLine(lambdaExpr.Compile().Invoke(new int[] { 10, 20, 30 }, 0, 5));
// 15

NewExpression

NewExpression newDictionaryExpression =Expression.New(typeof(Dictionary<int, string>));
Console.WriteLine(newDictionaryExpression.ToString());
// new Dictionary`2()

InvocationExpression

Expression<Func<int, int, bool>> largeSumTest =
    (num1, num2) => (num1 + num2) > 1000;

InvocationExpression invocationExpression= Expression.Invoke(
    largeSumTest,
    Expression.Constant(539),
    Expression.Constant(281));

Console.WriteLine(invocationExpression.ToString());
// Invoke((num1, num2) => ((num1 + num2) > 1000),539,281)

  前日我们演示了什么通过代码的方法去创设表明式树,然后总计了一下.NET为大家提供的表明式类型。下一篇,我们将连续研商发表式树的遍历问题,敬请期待,假如对于表明式树有趣味的同学欢迎持续关注~,