以下是我的问题的简单演示代码。
[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
- &gt; Search
- &gt; VisitConstant
在此执行路径中,传递给Search
方法的常量表达式(1L)是一个实常数值。在这里,一切都很好,断言按预期成功。通过第一条路径VisitConstant
调用node.Value.GetType()
时,Int64
为.Value
,1L
为TestParam
。
2。 GetStudent
- &gt; Search
- &gt; VisitConstant
- &gt; GetStudent
在此执行路径常量表达式(id:1L)中,Search
作为参数,并传递给闭包内的VisitConstant
方法。
问题
问题出在第二个执行路径上。通过第二条路径node.Value.GetType()
调用MyProject.Tests.ExpressionTests+<>c__DisplayClass0
为id
且此类有一个名为GetStudent
的公共字段(与1L
方法的参数相同),其值为id
。
问题
如何在第二条路径中获得DisplayClass
值?我知道闭包,node.Value.GetType().GetFields()[0].GetValue(node.Value);
是什么以及为什么它在编译时创建等等。我只对获取其字段值感兴趣。
我能想到的一件事是通过反思。有类似下面的东西,但它似乎并不整洁。
id
红利问题
在使用gettting VisitConstant
值的代码时,我更改了DisplayClass
方法,如下所示(虽然这不会解决我的问题)并得到一个例外,说“'object'不包含'id'“
奖金问题
由于动态在运行时得到解决,而dynamic
是在编译时创建的,为什么我们不能使用var st = new {Id = 1L};
object o = st;
dynamic dy = o;
Assert.AreEqual(1L, dy.Id);
访问其字段?虽然下面的代码工作,我希望代码也可以工作。
{{1}}
答案 0 :(得分:4)
Here is an article that explains how to do it,并包含执行此操作的代码。基本上,您可以做的是创建一个表示该子表达式的表达式,将其编译为委托,然后执行该委托。 (文章还解释了如何识别可以评估的子表达式,但我猜你对此不感兴趣。)
使用文章中的代码,将代码修改为以下代码将起作用:
private void Search<T>(Expression<Func<T, bool>> filter)
{
new MyExpressionVisitor().Visit(Evaluator.PartialEval(filter));
}
由于动态在运行时得到解决,
DisplayClass
是在编译时创建的,为什么我们不能使用dynamic
访问其字段?
由于DisplayClass
是嵌套在private
内的ExpressionTests
类,因此MyExpressionVisitor
内的代码无法访问其成员。
如果您在MyExpressionVisitor
内设置ExpressionTests
嵌套类,dynamic
将开始处理DisplayClass
。
匿名类型不会以这种方式运行,因为它们不会作为嵌套的private
类型发出。
答案 1 :(得分:3)
VisitConstant
在这里没有帮助,因为它收到一个构造成ConstantExpression
的编译器,它使用私有匿名类的对象来存储lambda被关闭的值(DisplayClassxxx
)
相反,我们应该覆盖VisitMember
方法并检查已经MemberExpression
作为内部ConstantExpression
的{{1}}。
这是一个很少反思的工作测试。
Expression