表达式树如何捕获局部变量

时间:2013-05-15 16:15:42

标签: .net expression-trees local-variables constant-expression

我正在努力创建动态表达式,我有以下场景,我希望有助于实现。

下式给出:

public class planet {
    public string name { get;set; }
}

class someTestClass {
    [Test]
    public void Planet_Exists_Statically(){
        var planetName = "earth";
        var planets = new List<planet> {new planet {name = planetName}};
        var found = planets.Exists(MakePredicate(planetName));
        Assert.IsTrue(found);
    }

    [Test]
    public void Planet_Exists_Statically(){
        var planetName = "earth";
        var planets = new List<planet> {new planet {name = planetName}};
        var found = planets.Exists(MakeDynamicPredicate(planetName));
        Assert.IsTrue(found);
    }

    private Predicate<planet> MakePredicate(string planetName){
        Expression<Predicate<planet>> pred = p => p.name == planetName;
        return pred.Compile();
    }

    private Predicate<planet> MakeDynamicPredicate(string planetName){
        var parm = Expression.Parameter(typeof(planet), "p")
        var pred = Expression.Lambda<Predicate<planet>>(
                    Expression.ReferenceEqual(
                        Expression.Property(parm, typeof(planet), "name"), 
                        **???WHAT GOES HERE???**,
                        parm);
        return pred.Compile();
    }
}

所以我的问题是我无法从MakeDynamicPredicate返回的谓词等于MakePredicate函数生成的谓词。

我读了一个reply post from Jon Skeet,但是不明白如何实现ConstantExpression以便捕获局部变量......

非常感谢任何帮助。

附加信息:前进我可能不知道正在使用的类,因此它最终将被抽象为更通用。

2 个答案:

答案 0 :(得分:1)

在您的情况下,您实际上不需要捕获任何局部变量,您只需使用Expression.Constant(planetName)

如果您随后使用MakeDynamicPredicate("Pluto")调用它,则生成的表达式就像您编写p => p.name == "Pluto"一样。

答案 1 :(得分:1)

正如你可以在Jon的帖子中看到的,你需要一些对象来存储你的变量。对于MakePredicate,它将由编译器创建,对于MakeDynamicPredicate,您必须自己完成。首先,我们需要一个用于变量值存储的类。


class someTestClass {
...
        private class ValueHolder
        {
            public string Value;
        }
...
}

现在,您将planetName放入ValueHolder的一个实例中,并创建一个Member Access表达式,以便在谓词表达式中使用它。


private Predicate MakeDynamicPredicate(string planetName){

        var valueHolder = new ValueHolder
        {
            Value = value
        };

        var valueExpr = Expression.MakeMemberAccess(
            Expression.Constant(valueHolder),
            valueHolder.GetType().GetField("Value"));

        var parm = Expression.Parameter(typeof(planet), "p")
        var pred = Expression.Lambda>(
                    Expression.ReferenceEqual(
                        Expression.Property(parm, typeof(planet), "name"), 
                        valueExpr,
                        parm);

}

P.S。查看编译器生成的代码(例如使用ILSpy)以更好地了解各种epxression的创建方式通常是有意义的。