ローカル変数を使用する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で何もしたくないのですが、 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
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ