로컬 변수를 사용하는 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 );
    }
  }

"arg.member == something"을 찾는 바이너리 op를 찾은 다음 오른쪽면을 컴파일 / 평가하고 두 예제 모두 결과를 문자열 "Michael"로 제공합니다.

참고로, 오른쪽면에 다음과 같이 lamda 인수를 사용하면 실패합니다.

p.FirstName == CallSomeFunc (p.FirstName)


인기 답변

편집 : 좋아, AHM의 의견 덕분에 당신이 지금 무엇을 의미하는지 명확하게 알 수 있습니다.

기본적으로 코드는 별도의 클래스에서 name 을 캡처하도록 컴파일 된 다음 필드 액세스를 적용하여 해당 인스턴스를 참조하는 상수 표현식에서 값을 가져옵니다. (식 만든 후에name 의 값을 변경할 수 있기 때문에이 작업을 수행해야하지만 식은 값이 아니라 변수를 캡처합니다.)

따라서 VisitConstantConstantExpression 에 대해 실제로 아무것도하고 싶지는 않습니다. VisitMember 의 필드 액세스에 대해 작업하고 VisitMember . ConstantExpression 하위에서 값을 가져온 다음 FieldInfo 에 값을 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
이 KB는 합법적입니까? 예, 이유를 알아보십시오.
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.