2

If I have a method that is building code using Expression trees, to handle runtime types, how can I make an Expression that references a parameter to the method?

E.g. in the below code, how do I build Expressions to pass in that reference the method parameters?

public static bool ExpressionContains(string s, string sub) {
    var cmi = typeof(String).GetMethod("Contains", new[] { typeof(string) });
    var body = Expression.Call(cmi, s ???, sub ???);

    return Expression.Lambda<Func<bool>>(body).Compile().Invoke();
}

1 Answer 1

3

Since the expression compiles to a Func<bool>, as far as it is concerned, the values of s and sub are constants:

public static bool ExpressionContains(string s, string sub) {
    var cmi = typeof(String).GetMethod("Contains", new[] { typeof(string) });
    var body = Expression.Call(
        Expression.Constant(s),
        cmi,
        Expression.Constant(sub));

    return Expression.Lambda<Func<bool>>(body).Compile().Invoke();
}

If you wanted to compile a Func<string, string, bool> where s and sub were passed in, then:

public static bool ExpressionContains(string s, string sub) {
    var sExpr = Expression.Parameter(typeof(string), "s");
    var subExpr = Expression.Parameter(typeof(string), "sub");
    var cmi = typeof(String).GetMethod("Contains", new[] { typeof(string) });
    var body = Expression.Call(sExpr, cmi, subExpr);

    return Expression.Lambda<Func<string, string, bool>>(body, new[] { sExpr, subExpr }).Compile().Invoke(s, sub);
}
Sign up to request clarification or add additional context in comments.

5 Comments

Interestingly (to me), an Expression built from a lambda uses a MemberAccess FieldExpression and not a ConstantExpression.
The lambda will have captured s, and the way that delegates capture locals is that the locals get lifted into fields in a new class, and the delegate method gets defined on that class, so I bet something similar is happening to you. The class the field is on will be a constant though surely. That does need the ability to rewrite the code around the lambda, which you don't get if you're writing expressions by hand, so it's not applicable to your question. I don't see the problem with a list though? The reference itself is a constant, but the underlying list is still the same mutable object.
I'm afraid I'm on mobile now and can't add to my answer until tomorrow evening, but change my first snippet so it takes a list of strings and a string, and calls list's Add method on that list passing the string. It will mutate the list that the caller passed in.
In my scenario this should work fine, but I could see where not lifting could cause a problem in some scenarios, but sounds like it would be a lot of work to fix.
You will only get the compiler to do that lifting for you if you write a lambda. If you're writing expressions by hand, you'll have to do the lifting by hand: by getting the caller to construct a mutable container containing whatever variables you want to be able to reassign.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.