2013-01-11 133 views
4

我已经构建了自己的SQL查询构建器来分解表达式,但是,我在尝试获取在与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 

它传回空。

上述方法工作正常,如果我把lambda“字符串”放在类中,但不起作用,如果他们的字符串是在控制台函数中定义的。

任何人都可以告诉我如何获得字符串值,如果它定义在lambda的函数级别?

编辑:新增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? 
    } 
} 

EDITED

添加了代码的控制台应用程序为例,说明什么可行,什么不可行。

+1

我认为你正在讨论这个错误。你应该试图找到'ConstantExpression'并从中获取值。我建议不要使用反射,因为这是重新发明轮子的方式(当然,如果您在表达式而不是字段/属性间进行错误表达,那么根本没有任何帮助)。然而,为了帮助更多,你能否提供更多关于你的builder代码的上下文 - 它在哪里得到'node'等? –

+0

“节点”在VisitMember的重写中传递,并且是MemberExpression类型。 – Sean

+0

因此询问该节点更多 - 它是* tree *,其中一个子节点将是'ConstantExpression'的一个实例,它具有'object'类型的'Value'属性,该属性将包含文字'testValue'。 (实际上,你正在关闭一个变量,所以它可能是一个编译器生成的类型;可能你甚至需要'GetField'而不是 - 如果编译器生成带有字段或属性的类,我就会想到) –

回答

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")); 
+0

有一点调整,这工作!谢谢! – Sean