If I have an expression of a function delegate that takes a number of parameters like so:
Expression<Func<int, int, int, bool>> test = (num1, num2, num3) => num1 + num2 == num3;
is there a way / how can I substitute one of the values (say 5 for num1
) and get the equivalent expression to:
Expression<Func<int, int, bool>> test = (num2, num3) => 5 + num2 == num3;
EDIT:
Also needs to resolve complex types, e.g.:
Expression<Func<Thing, int, int>> test = (thing, num2) => thing.AnIntProp + num2;
My answer was to use a an expression visitor. (thanks to @Alexei-levenkov for pointing it out).
The answer for my particular situation was a little different than for the simplified example I used in the question. But, for completeness, here was 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 I borrowed from this gist by jonothanconway.
In my situation I wanted to replace use of a a complex type. e.g:
Expression<Func<Thing, int, bool>> test = (thing, num) => thing.AnIntProperty == num;
So I did an override of the VisitMember method. This is still a work in progress but 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 only resolve a field or property. Next step might be to add support for a method (but as they have params themselves, so that'll take some more work...)
EDIT: The above Member visitor solution also wouldn't support passing an Object itself into a method call. e.g. (x, thing) => x.DoSomething(thing)
so a modification would be needed to do that also.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With