John Wu
John Wu

Reputation: 52240

How can I evaluate part of an Expression that references a parameter?

I am writing a method intended to parse a method call expression and determine (1) the method being called and (2) the arguments being passed. I can retrieve the method just fine but am having trouble evaluating the argument expressions.

Here is the code:

public static void InspectExpression(Expression<Action<Input>> expression)
{
    var memberExpression = expression.Body as MethodCallExpression;
    var methodName = memberExpression.Method.Name;
    Console.WriteLine("The expression calls the {0} method.", methodName);

    var argumentExpression = memberExpression.Arguments[0];
    var argumentLambda = Expression.Lambda<Func<string>>(argumentExpression);
    var compiled = argumentLambda.Compile();
    var argumentValue = compiled();

    Console.WriteLine("The expression would pass {0} as the first argument to {1}", argumentValue, methodName);
}

I am calling it with the following code:

public class Input
{
    public string SomeProperty { get; set; }
}

public static void MethodName(string arg)
{
    //Doesn't matter what this does
}

public static void Main()
{
    InspectExpression( i => MethodName(i.SomeProperty) );
}

However this yields the following exception:

[System.InvalidOperationException: variable 'i' of type 'Input' referenced from scope '', but it is not defined]

This makes sense since there is no way to evaluate that expression without knowing the value of the input, since the argument is computed based on the expression's input.

So I tried adding the input like this:

public static void InspectExpression(Expression<Action<Input>> expression)
{
    var memberExpression = expression.Body as MethodCallExpression;
    var methodName = memberExpression.Method.Name;
    Console.WriteLine("The expression calls the {0} method.", methodName);

    var argumentExpression = memberExpression.Arguments[0];
    var argumentLambda = Expression.Lambda<Func<Input,string>>(argumentExpression);
    var compiled = argumentLambda.Compile();

    var sampleInput = new Input { SomeProperty = "Hello world" };
    var argumentValue = compiled(sampleInput);

    Console.WriteLine("The expression would pass {0} as the first argument to {1}", argumentValue, methodName);
}

This raised the exception:

[System.ArgumentException: Incorrect number of parameters supplied for lambda declaration]

I'm guessing I have to add a parameter reference to the expression somehow, but I'm at a loss exactly how to do this.

Give a sample input, how can I retrieve the computed argument to this expression?

Here is a link to a Fiddle in case you'd like to try it out or use it as the basis of an example solution.

Upvotes: 0

Views: 251

Answers (1)

Iridium
Iridium

Reputation: 23721

You'll need to pass a set of parameters to the Expression.Lambda<...>() call - you should be able to use the same set of parameters as the original expression, so change your definition of argumentLambda to:

var argumentLambda = Expression.Lambda<Func<Input,string>>(argumentExpression, expression.Parameters);

Upvotes: 1

Related Questions