My answer was to use visitor expressions. (thanks @ Alexey-Levenkov for pointing).
The answer to my specific situation was slightly different than for the simplified example that I used in the question. But, for completeness, here's how I did it:
public class ResolveParameterVisitor : ExpressionVisitor { private readonly ParameterExpression _param; private readonly object _value; public ResolveParameterVisitor(ParameterExpression param, object value) { _param = param; _value = value; } public Expression ResolveLocalValues(Expression exp) { return Visit(exp); } protected override Expression VisitParameter(ParameterExpression node) { if (node.Type == _param.Type && node.Name == _param.Name && node.Type.IsSimpleType()) { return Expression.Constant(_value); } return base.VisitParameter(node); } protected override Expression VisitLambda<T>(Expression<T> node) { var parameters = node.Parameters.Where(p => p.Name != _param.Name && p.Type != _param.Type).ToList(); return Expression.Lambda(Visit(node.Body), parameters); } }
Note that IsSimpleType is an extension that I borrowed from this jonothanconway meaning .
In my situation, I wanted to replace using a complex type. eg:
Expression<Func<Thing, int, bool>> test = (thing, num) => thing.AnIntProperty == num;
So, I did an override of the VisitMember method. This is still ongoing, but it looks like this:
protected override Expression VisitMember(MemberExpression m) { if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter && m.Expression.Type == _param.Type && ((ParameterExpression)m.Expression).Name == _param.Name) { object newVal; if (m.Member is FieldInfo) newVal = ((FieldInfo)m.Member).GetValue(_value); else if (m.Member is PropertyInfo) newVal = ((PropertyInfo)m.Member).GetValue(_value, null); else newVal = null; return Expression.Constant(newVal); } return base.VisitMember(m); }
This will allow the field or property. The next step may be to add support for the method (but since they have the parameters themselves, so it will take another job ...)
EDIT: The Member visitor's above solution will also not support passing the object itself to a method call. for example (x, thing) => x.DoSomething(thing) , so this will require modification.
Simon c
source share