4

式を分解する独自のSQLクエリビルダーを作成しましたが、ラムダ式と同じ関数で定義された文字列の値を取得しようとすると問題が発生します。

これが私がコンソールアプリでやろうとしていることです:

private static void MyBuilderTest()
{
    var sqlBuilder = new SqlBuilder();

    // Doesn't work -- NEED GUIDANCE HERE
    var testValue = "Test";  // Defined in the same function as the lambda below

    sqlBuilder.Select<FooObject>(o => o.FooValue == testValue);

    // Works
    var someObject = new SomeObject { SomeValue = "classTest };

    sqlBuilder.Select<FooObject>(o => o.FooValue == someObject.SomeValue);
}

私のビルダーでは、ExpressionVisitorからサブクラス化し、VisitMemberをオーバーライドします。基本コンソールレベルで定義された文字列は、次のように返されることがわかりました。

Node.Expression.NodeType == ExpressionType.Constant

Node.Expressionは、次のプロパティを返します。

CanReduce = false
DebugView = ".Constant<ConsoleApplication1.Program+<>c__DisplayClass1>(ConsoleApplication1.Program+<>c__DisplayClass1)"
NodeType = Constant
Type = System.Type {System.RunetimeType}
Value = {ConsoleApplication1.Program}

Node.Expression.Valueには次のものが含まれます。

testValue = "Test" (Type: string)

この値を取得するにはどうすればよいですか? 私は次のようないくつかのことを試しました:

var memberType = node.Expression.Type.DeclaringType;

これにより、ConsoleApplication1.Programタイプが返されます。

しかし、私がそうするとき:

 memberType.GetProperty("testValue");   // Declaring Type from Expression

nullを返します。

上記のメソッドは、ラムダの「文字列」をクラスに配置すると正常に機能しますが、文字列がコンソール関数で定義されている場合は機能しません。

ラムダの関数レベルで定義されている場合、文字列値を取得する方法を教えてもらえますか?

編集:VisitMemberを追加

protected override Expression VisitMember(MemberExpression node)
{
    if (node.NodeType == ExpressionType.Constant)
    {
        // Node.Expression is a ConstantExpression type.
        // node.Expression contains properties above
        // And Has Value of:  {ConsoleApplication1.Program}
        // Expanding Value in Watch window shows:  testValue = "Test"

        // How do I get this value, if the ConsoleApplication1.Program type doesn't
        // even know about it?  Looks like maybe a dynamic property?
    }
 }

編集済み

コンソールアプリの例にコードを追加して、何が機能し、何が機能しないかを示します。

4

2 に答える 2

2

この例のラムダは変数を「クローズ」しました。testValueこれは、コンパイラが、と呼ばれる自動生成されたクラスの同じ名前のフィールドとして変数をキャプチャしたことを意味しますConsoleApplication1.Program+<>c__DisplayClass1>。通常のリフレクションを使用して、バイナリ式の右辺をMemberExpressionにキャストすることにより、そのフィールドの現在の値を取得できます。

var testValue = "hello";
var expr = (Expression<Func<string, bool>>) (x => x == testValue);
var rhs = (MemberExpression) ((BinaryExpression) expr.Body).Right;
var obj = ((ConstantExpression) rhs.Expression).Value;
var field = (FieldInfo) rhs.Member;
var value = field.GetValue(obj);
Debug.Assert(Equals(value, "hello"));
testValue = "changed";
value = field.GetValue(obj);
Debug.Assert(Equals(value, "changed"));

または、変数を定数に変更することもできます。

const string testValue = "hello";
var expr = (Expression<Func<string, bool>>) (x => x == testValue);
var value = ((ConstantExpression) ((BinaryExpression) expr.Body).Right).Value;
Debug.Assert(Equals(value, "hello"));
于 2013-01-11T01:16:47.413 に答える
0

自分でこれを行う代わりに、MattWarrenPartialEvaluatorから見てください。定数へのすべての参照を定数自体に置き換えます。

于 2013-01-11T01:20:58.923 に答える