Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

System.Linq.Expressions: Binding LambdaExpression inputs at runtime

Tags:

c#

.net

Consider the following setup:

class A { public int x; }
class B { public int y; }

static class Helper
{
    public static Expression<Func<B>> BindInput(
        Expression<Func<A, B>> expression,
        A input)
    {
        //TODO
    }
}

static void Main(string[] args)
{
    Expression<Func<B>> e = Helper.BindInput(
        (A a) => new B { y = a.x + 3 },
        new A { x = 4 });

    Func<B> f = e.Compile();
    Debug.Assert(f().y == 7);
}

What I want to do in the method BindInput is transform the expression to have input embedded into it. In the example usage in Main, the resulting expression e would be

() => new B { y = input.x + 3 }

where input is the second value that was passed into BindInput.

How would I go about doing this?

Edit:

I should add that the following expression e is not what I'm looking for:

((A a) => new B { y = a.x + 3 })(input)

This would be fairly trivial to obtain because it just involves adding a layer on top of the existing expression.

like image 678
Timothy Shields Avatar asked Oct 24 '25 10:10

Timothy Shields


1 Answers

After lots of searching I stumbled across the magic ExpressionVisitor class. The following seems to be working perfectly:

class MyExpressionVisitor : ExpressionVisitor
{
    public ParameterExpression TargetParameterExpression { get; private set; }
    public object TargetParameterValue { get; private set; }
    public MyExpressionVisitor(ParameterExpression targetParameterExpression, object targetParameterValue)
    {
        this.TargetParameterExpression = targetParameterExpression;
        this.TargetParameterValue = targetParameterValue;
    }
    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (node == TargetParameterExpression)
            return Expression.Constant(TargetParameterValue);
        return base.VisitParameter(node);
    }
}

static class Helper
{
    public static Expression<Func<B>> BindInput(Expression<Func<A, B>> expression, A input)
    {
        var parameter = expression.Parameters.Single();
        var visitor = new MyExpressionVisitor(parameter, input);
        return Expression.Lambda<Func<B>>(visitor.Visit(expression.Body));
    }
}
like image 158
Timothy Shields Avatar answered Oct 25 '25 23:10

Timothy Shields