如何获取使用局部变量的ConstantExpression的值?

c# c#-4.0 expression-trees

我创建了一个覆盖VisitConstant的ExpressionVisitor实现。但是,当我创建一个利用局部变量的表达式时,我似乎无法获得变量的实际值。

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

string name = "Michael";

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

我如何在ConstantExpression中获取变量“name”的值?我唯一能想到的是:

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

显然,这并不适合非常灵活....

稍微复杂的例子如下:

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

一般承认的答案

以下是我为您列出的两种情况解决的问题。

基本上假设'=='的右侧可以被视为不带参数并返回值的函数,它可以编译为C#委托并调用以检索此值而无需担心代码上的确切内容右手边。

所以基本的示例代码如下

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 );
    }
  }

它查找二进制op寻找“arg.member == something”然后只编译/评估右侧,对于这两个例子,你提供的结果是一个字符串“Michael”。

注意,如果您的右侧涉及使用lamda参数,则会失败

p.FirstName == CallSomeFunc(p.FirstName)


热门答案

编辑:好的,由于AHM的评论,你现在的意思更清楚了。

基本上,代码被编译为在单独的类中捕获name - 然后应用字段访问以从引用它的实例的常量表达式获取其值。 (必须这样做,因为您可以创建表达式更改name的值 - 但表达式捕获变量,而不是值。)

因此,您实际上并不想对VisitConstantConstantExpression执行任何VisitConstant - 您希望在VisitMember进行字段访问。您需要从ConstantExpression子项中获取值,然后将其提供给FieldInfo以获取值:

using System;
using System.Linq.Expressions;
using System.Reflection;

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

static class Program
{
    static void Main(string[] args)
    {
        string name = "Michael";

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

        new Visitor().Visit(exp);
    }
}

class Visitor : ExpressionVisitor    
{
    protected override Expression VisitMember
        (MemberExpression member)
    {
        if (member.Expression is ConstantExpression &&
            member.Member is FieldInfo)
        {
            object container = 
                ((ConstantExpression)member.Expression).Value;
            object value = ((FieldInfo)member.Member).GetValue(container);
            Console.WriteLine("Got value: {0}", value);
        }
        return base.VisitMember(member);
    }
}

编辑:好的,稍微涉及访问者类的版本:

class Visitor : ExpressionVisitor    
{
    protected override Expression VisitMember
        (MemberExpression memberExpression)
    {
        // Recurse down to see if we can simplify...
        var expression = Visit(memberExpression.Expression);

        // If we've ended up with a constant, and it's a property or a field,
        // we can simplify ourselves to a constant
        if (expression is ConstantExpression)
        {
            object container = ((ConstantExpression) expression).Value;
            var member = memberExpression.Member;
            if (member is FieldInfo)
            {
                object value = ((FieldInfo)member).GetValue(container);
                return Expression.Constant(value);
            }
            if (member is PropertyInfo)
            {
                object value = ((PropertyInfo)member).GetValue(container, null);
                return Expression.Constant(value);
            }
        }
        return base.VisitMember(memberExpression);
    }
}

现在运行:

var localPerson = new Person { FirstName = "Jon" };

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

Console.WriteLine("Before: {0}", exp);
Console.WriteLine("After: {0}", new Visitor().Visit(exp));

给出结果:

Before: p => Convert((p.FirstName == 
           value(Program+<>c__DisplayClass1).localPerson.FirstName))
After: p => Convert((p.FirstName == "Jon"))


许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow
许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow