Question

I'm currently working towards creating dynamic expressions and I have the following scenario, which I'd like help to achieve.

given:

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();
    }
}

So my issue is that I can't get the predicate returned from the MakeDynamicPredicate to be equal to the one generated by the MakePredicate function.

I read a reply post from Jon Skeet, but don't understand how to implement the ConstantExpression so that the local variable is captured...

Any help would be greatly appreciated.

Additional info: going forward I may not know the class that is being used so it will eventually be abstracted to be more generic.

Was it helpful?

Solution

In your case, you don't actually need to capture any local variable, you can just use Expression.Constant(planetName).

If you then call it with, for example, MakeDynamicPredicate("Pluto"), the generated expression will be as if you wrote p => p.name == "Pluto".

OTHER TIPS

As you can read in Jon's post you need some object to store your variable. For MakePredicate it will be created by compiler, for MakeDynamicPredicate you'll have to do it yourself. First of all we need a class for variable value storage.


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

Now you put your planetName into an instance of the ValueHolder and make a Member Access expression for using it in your predicate expression.


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. it often makes sense to take a look at the compiler generated code (e.g. with ILSpy) to better understand how various epxressions are created.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top