如何獲取使用局部變量的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