防爆pression /声明树木 [英] Expression/Statement trees

查看:118
本文介绍了防爆pression /声明树木的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新问题进一步回落

我一直在尝试与前pression树在.NET 4生成code运行时,我一直在试图实施的foreach 声明通过建立一个前pression树。

I've been experimenting with expression trees in .NET 4 to generate code at runtime and I've been trying to implement the foreach statement by building an expression tree.

在最后,前pression应该能够生成一个委托,做这样的:

In the end, the expression should be able to generate a delegate that does this:

Action<IEnumerable<int>> action = source => 
{
  var enumerator = source.GetEnumerator();
  while(enumerator.MoveNext())
  {
    var i = enumerator.Current;
    // the body of the foreach that I don't currently have yet
  }
}

我拿出其产生BlockEx pression从IEnumerable以下的辅助方法:

I've come up with the following helper method that generates a BlockExpression from an IEnumerable:

public static BlockExpression ForEachExpr<T>(this IEnumerable<T> source, string collectionName, string itemName)
{
        var item = Expression.Variable(typeof(T), itemName);

        var enumerator = Expression.Variable(typeof(IEnumerator<T>), "enumerator");

        var param = Expression.Parameter(typeof(IEnumerable<T>), collectionName);

        var doMoveNext = Expression.Call(enumerator, typeof(IEnumerator).GetMethod("MoveNext"));

        var assignToEnum = Expression.Assign(enumerator, Expression.Call(param, typeof(IEnumerable<T>).GetMethod("GetEnumerator")));

        var assignCurrent = Expression.Assign(item, Expression.Property(enumerator, "Current"));

        var @break = Expression.Label();

        var @foreach = Expression.Block(
            assignToEnum,
            Expression.Loop(
                Expression.IfThenElse(
                Expression.NotEqual(doMoveNext, Expression.Constant(false)),
                    assignCurrent
                , Expression.Break(@break))
            ,@break)
        );
        return @foreach;

}

下面code:

The following code:

var ints = new List<int> { 1, 2, 3, 4 };
var expr = ints.ForEachExpr("ints", "i");
var lambda = Expression.Lambda<Action<IEnumerable<int>>>(expr, Expression.Parameter(typeof(IEnumerable<int>), "ints"));

生成此EX pression树:

Generates this expression tree:

.Lambda #Lambda1<System.Action`1[System.Collections.Generic.IEnumerable`1[System.Int32]]>(System.Collections.Generic.IEnumerable`1[System.Int32] $ints)
{
    .Block() {
        $enumerator = .Call $ints.GetEnumerator();
        .Loop  {
            .If (.Call $enumerator.MoveNext() != False) {
                $i = $enumerator.Current
            } .Else {
                .Break #Label1 { }
            }
        }
        .LabelTarget #Label1:
    }
}

这似乎是好的,但调用编译上EX pression导致异常:

This seems to be OK, but calling Compile on that expression results in an exception:

"variable 'enumerator' of type 'System.Collections.Generic.IEnumerator`1[System.Int32]' referenced from scope '', but it is not defined"

我不是在这里定义:

Didn't I define it here:

    var enumerator = Expression.Variable(typeof(IEnumerator<T>), "enumerator");

当然,这里的例子是人为的,不具有实用化,但我试图让有机构EX pression树的窍门,以动态地将它们组合在运行时未来。

Of course, the example here is contrived and doesn't have a practical use yet, but I'm trying to get the hang of expression trees that have bodies, in order to dynamically combine them at runtime in the future.


编辑:我最初的问题,解决了亚历山德拉,谢谢!当然,我现在已经运行到下一个问题。我声明了一个 BlockEx pression 已在它的变量。在那个EX pression,我想另一位前pression引用该变量。但我没有实际引用该变量,只是它的名字,因为前pression外部提供。

My initial problem was solved by Alexandra, thanks! Of course, I've run into the next problem now. I've declared a BlockExpression that has a variable in it. Inside that expression, I want another expression that references that variable. But I don't have an actual reference to that variable, just its name, because the expression is supplied externally.

var param = Expression.Variable(typeof(IEnumerable<T>), "something");

var block = Expression.Block(
                new [] { param },
                body
            );

变量传递外,并没有直接引用参数,但不知道名字在EX pression变量(东西)。它看起来是这样的:

The body variable is passed in externally and has no direct reference to param, but does know the name of the variable in the expression ("something"). It looks like this:

var body = Expression.Call(typeof(Console).GetMethod("WriteLine",new[] { typeof(bool) }), 
               Expression.Equal(Expression.Parameter(typeof(IEnumerable<int>), "something"), Expression.Constant(null)));

这是code,这产生:

This is the "code" that this generates:

.Lambda #Lambda1<System.Action`1[System.Collections.Generic.IEnumerable`1[System.Int32]]>(System.Collections.Generic.IEnumerable`1[System.Int32] $something)
{
    .Block(System.Collections.Generic.IEnumerable`1[System.Int32] $something) {
        .Call System.Console.WriteLine($something== null)
    }
}

然而,它并不能编译。由于相同的错误了。

However, it doesn't compile. With the same error as before.

TLDR:如何在一个前pression树引用变量被识别?

TLDR: How do I reference a variable by identifier in an expression tree?

推荐答案

您的问题是,我们还没有传入的参数和变量的块的前pression。您可以使用他们的内部EX pressions,但该块前pression知道见死不救。基本上,所有你需要做的是所有的参数和变量传递给一个块的前pression。

You problem is that you didn't pass parameters and variables to your block expression. You use them in the "inner" expressions, but the block expression knows nothing about them. Basically, all you need to do is to pass all your parameters and variables to a block expression.

        var @foreach = Expression.Block(
            new ParameterExpression[] { item, enumerator, param },
            assignToEnum,
            Expression.Loop(
                Expression.IfThenElse(
                    Expression.NotEqual(doMoveNext, Expression.Constant(false)),
                    assignCurrent,
                    Expression.Break(@break))
            , @break)
        );

这篇关于防爆pression /声明树木的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆