Дерево выражений, как мне захватить локальную переменную

В настоящее время я работаю над созданием динамических выражений, и у меня есть следующий сценарий, в котором я хотел бы помочь.

дано:

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.

Я прочитал ответ от Джона Скита, но не понимаю, как реализовать ConstantExpression, чтобы локальная переменная была захвачена. .

Любая помощь будет принята с благодарностью.

Дополнительная информация: в будущем я могу не знать, какой класс используется, поэтому в конечном итоге он будет абстрагирован, чтобы быть более общим.


person ermagana    schedule 15.05.2013    source источник
comment
Если вы упомянете какой-то пост (от Джона Скита), было бы полезно, если бы вы дали ссылку на него.   -  person svick    schedule 15.05.2013
comment
извините, я пошел дальше и сделал имя Джона ссылкой на ответ, который он предоставил   -  person ermagana    schedule 15.05.2013


Ответы (2)


В вашем случае вам не нужно захватывать какую-либо локальную переменную, вы можете просто использовать Expression.Constant(planetName).

Если вы затем вызовете его, например, с помощью MakeDynamicPredicate("Pluto"), сгенерированное выражение будет таким, как если бы вы написали p => p.name == "Pluto".

person svick    schedule 15.05.2013
comment
спасибо за предложение, но я хочу, чтобы выражение возвращалось как p => p.name == planetName - person ermagana; 15.05.2013
comment
@ermagana Почему? В вашем случае это не имело бы никакого значения. - person svick; 15.05.2013
comment
этот конкретный экземпляр не имел бы значения, но я стремлюсь к более общему дизайну, я также хотел бы знать, как это сделать в целом, поскольку это должно быть возможно, по крайней мере, из того, что я понимаю . - person ermagana; 15.05.2013

Как вы можете прочитать в сообщении Джона, вам нужен какой-то объект для хранения вашей переменной. Для 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), чтобы лучше понять, как создаются различные выражения.

person Konstantin Oznobihin    schedule 17.05.2013