带有字符串赋值和获取值的表达式树

时间:2013-01-11 00:48:48

标签: c# linq expression-trees

我已经构建了自己的SQL查询构建器,它拆分了一个Expression,但是,我在尝试获取与lambda表达式相同的函数中定义的字符串值时遇到了问题。

以下是我在控制台应用中尝试做的事情:

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。

如果我将lambda“strings”放在一个类中,上面的方法可以正常工作,但如果在控制台函数中定义了string,则上述方法不起作用。

如果在lambda的函数级别定义了字符串值,有人可以告诉我如何获取字符串值吗?

已编辑:已添加了访问者记录

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?
    }
 }

EDITED

在控制台应用示例中添加了代码,以显示哪些有效,哪些无效。

2 个答案:

答案 0 :(得分:2)

示例中的lambda已“关闭”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"));

答案 1 :(得分:0)

不要自己做,而是看看PartialEvaluator from Matt Warren。它用常量本身替换所有对常量的引用。