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

Viewed 11707

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;
6 Answers

Here another solution without DynamicInvoke Because the tree is traversed recursively, no stack is requried. If a member of a constant or of a static method is accessed, the value is evaluated after traversing the child nodes.

This replaces expressions like

var myFoo = new Foo{ OtherObjects = new List<OtherObjec>{ new OtherObject {Prop = 5 }};
var expression = () => myFoo.OtherObjects[0].Prop
--->
var expression = () => 5

This method is not complete, and does not handle all complicated cases but it can be a good start for other looking for a solution to this problem

public class ReplaceConstantVisitor : ExpressionVisitor
{
    private int m_memberAccessDepth = 0;
    private object m_constantValue;
    private bool m_constantOnStack = false;

    public ReplaceConstantVisitor()
    {
        m_constantValue = null;
    }

    protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
    {
        var result = base.VisitMethodCall(methodCallExpression);
        if (m_constantOnStack)
        {
            var reducedArguments = methodCallExpression.Arguments.OfType<ConstantExpression>().ToList();
            if (reducedArguments.Count() == methodCallExpression.Arguments.Count)
            {
                m_constantValue = methodCallExpression.Method.Invoke(m_constantValue, reducedArguments.Select(x => x.Value).ToArray());
            }
            else
            {
                m_constantOnStack = false;
            }
        }
        return result;
    }
    
    protected override Expression VisitMember(MemberExpression memberExpression)
    {
        m_memberAccessDepth++;
        var result = base.VisitMember(memberExpression);
        m_memberAccessDepth--;

        // initial condition to do replacement
        switch (memberExpression.Expression)
        {
            // replace constant member access
            case ConstantExpression constantExpression:
                m_constantOnStack = true;
                m_constantValue = constantExpression.Value;
                break;
            // replace static member access
            case null when memberExpression.Member.IsStatic():
                m_constantOnStack = true;
                break;
        }

        if (m_constantOnStack)
        {
            switch (memberExpression.Member)
            {
                case PropertyInfo propertyInfo:
                    m_constantValue = propertyInfo.GetValue(m_constantValue);
                    break;
                case FieldInfo fieldInfo:
                    m_constantValue = fieldInfo.GetValue(m_constantValue);
                    break;
                default:
                    m_constantOnStack = false; // error case abort replacement
                    break;
            }
        }

        if (m_constantOnStack && m_memberAccessDepth == 0)
        {
            m_constantOnStack = false;
            var constant = m_constantValue;
            m_constantValue = null;
            return Expression.Constant(constant);
        }
        return result;
    }
}
Related