[TestClass]
public class ExpressionTests
{
[TestMethod]
public void TestParam()
{
Search<Student>(s => s.Id == 1L);
GetStudent(1L);
}
private void GetStudent(long id)
{
Search<Student>(s => s.Id == id);
}
private void Search<T>(Expression<Func<T, bool>> filter)
{
var visitor = new MyExpressionVisitor();
visitor.Visit(filter);
}
}
public class MyExpressionVisitor : ExpressionVisitor
{
protected override Expression VisitConstant(ConstantExpression node)
{
Assert.AreEqual(1L, node.Value);
return base.VisitConstant(node);
}
}
TestParam
方法会导致VisitConstant
在两个不同的路径上被调用:
1. TestParam
-> Search
-> VisitConstant
在这个执行路径中,传递给Search
方法的常量表达式(1L)是一个真实的常量值。在这里,一切都很好,断言成功。当通过第一个路径调用VisitConstant
时,node.Value.GetType()
是Int64
,它的.Value
是1L
。
2. TestParam
-> GetStudent
-> Search
-> VisitConstant
在这个执行路径中,常量表达式(id: 1L)被GetStudent
作为参数接收,并在闭包内传递给Search
方法。
问题
问题出现在第二个执行路径上。当通过第二个路径调用VisitConstant
时,node.Value.GetType()
是MyProject.Tests.ExpressionTests+<>c__DisplayClass0
,而这个类有一个名为id
的公共字段(与GetStudent
方法的参数相同),其值为1L
。
问题
我该如何在第二个路径中获取id
的值?我知道闭包是什么,知道DisplayClass
是在编译时创建的等等。我只想获取它的字段值。我能想到的一件事是通过反射来实现。类似下面的代码,但这似乎不太好。
node.Value.GetType().GetFields()[0].GetValue(node.Value);
奖励问题
在处理获取id
值的代码时,我修改了VisitConstant
方法如下(虽然这不会解决我的问题),并出现了一个异常,表示"'object' does not contain a definition for 'id'"
奖励问题
由于动态类型是在运行时解析的,而DisplayClass
是在编译时创建的,为什么我们不能使用dynamic
访问它的字段?尽管下面的代码可以工作,但我期望上面的代码也能工作。
var st = new {Id = 1L};
object o = st;
dynamic dy = o;
Assert.AreEqual(1L, dy.Id);