Search code examples
c#c#-4.0expression-trees

How to get the value of a ConstantExpression which uses a local variable?


I created an ExpressionVisitor implementation that overrides VisitConstant. However, when I create an expression that utilizes a local variable I can't seem to get the actual value of the variable.

public class Person
{
  public string FirstName { get; set; }
}

string name = "Michael";

Expression<Func<Person, object>> exp = p => p.FirstName == name;

How in the world do I get the value of the variable "name" from the ConstantExpression? The only thing that I can think of is this:

string fieldValue = value.GetType().GetFields().First().GetValue(value).ToString();

Obviously this doesn't lend itself to being very flexible though....

A slightly more complicated example would be the following:

Person localPerson = new Person { FirstName = "Michael" };
Expression<Func<Person, object>> exp = p => p.FirstName == localPerson.FirstName;

Solution

  • Here's how I solved it for both cases you listed.

    Basically assuming that the right hand side of your '==' can be treated like a function that takes no arguments and returns a value, it can be compiled to a C# delegate and invoked to retrieve this value without worrying about exactly what the code on the right hand side does.

    So the basic example code is below

    class Visitor : ExpressionVisitor {
    
      protected override Expression VisitBinary( BinaryExpression node ) {
    
        var memberLeft = node.Left as MemberExpression;
        if ( memberLeft != null && memberLeft.Expression is ParameterExpression ) {
    
          var f = Expression.Lambda( node.Right ).Compile();
          var value = f.DynamicInvoke();
          }
    
        return base.VisitBinary( node );
        }
      }
    

    It looks for a binary op looking for "arg.member == something" then just compiles/evaluates the right hand side, and for both examples your provide the result is a string "Michael".

    Note, this fails if your right hand side involved using the lamda argument like

    p.FirstName == CallSomeFunc( p.FirstName )