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

dev*_*ife 19 c# expression-trees c#-4.0

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

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

string name = "Michael";

Expression<Func<Person, object>> exp = p => p.FirstName == name;
Run Code Online (Sandbox Code Playgroud)

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

string fieldValue = value.GetType().GetFields().First().GetValue(value).ToString();
Run Code Online (Sandbox Code Playgroud)

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

一个稍微复杂的例子如下:

Person localPerson = new Person { FirstName = "Michael" };
Expression<Func<Person, object>> exp = p => p.FirstName == localPerson.FirstName;
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 32

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

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

所以你实际上并不希望将上做任何事情ConstantExpressionVisitConstant-你想在现场访问工作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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在运行:

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));
Run Code Online (Sandbox Code Playgroud)

给出结果:

Before: p => Convert((p.FirstName == 
           value(Program+<>c__DisplayClass1).localPerson.FirstName))
After: p => Convert((p.FirstName == "Jon"))
Run Code Online (Sandbox Code Playgroud)


Mer*_*OWA 17

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

基本上假设'=='的右侧可以被视为不带参数并返回值的函数,它可以编译为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 );
    }
  }
Run Code Online (Sandbox Code Playgroud)

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

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

p.FirstName == CallSomeFunc(p.FirstName)

  • @SergeiKuz'michev 我同意,但我的回答侧重于灵活性,而不是速度。通过对预期表达式类型的假设,您当然可以比 Compile &amp; DynamicInvoke 做得更好。 (2认同)