我已经构建了自己的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
节点。表达式返回
的属性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。程序类型。
但是,当我这样做时:
memberType.GetProperty("testValue"); // Declaring Type from Expression
返回null
如果我将lambda "strings"放在类中,上述方法可以正常工作,但如果它们的字符串在控制台函数中定义,则不起作用。
谁能告诉我如何得到字符串值,如果它定义在lambda的函数级别?
EDITED: add 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?
}
}
在控制台应用程序示例中添加代码,以显示哪些可行,哪些不可行。
示例中的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"));
与其这样做,不如看看Matt Warren的PartialEvaluator
。它用常量本身替换所有对常量的引用。