由于闭包而将实际值包装到DisplayClass中时获取ConstantExpression.Value

时间:2014-09-06 22:27:53

标签: c# dynamic closures linq-expressions expressionvisitor

以下是我的问题的简单演示代码。

[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.Value1LTestParam

2。 GetStudent - &gt; Search - &gt; VisitConstant - &gt; GetStudent

在此执行路径常量表达式(id:1L)中,Search作为参数,并传递给闭包内的VisitConstant方法。

问题

问题出在第二个执行路径上。通过第二条路径node.Value.GetType()调用MyProject.Tests.ExpressionTests+<>c__DisplayClass0id且此类有一个名为GetStudent的公共字段(与1L方法的参数相同),其值为id

问题

如何在第二条路径中获得DisplayClass值?我知道闭包,node.Value.GetType().GetFields()[0].GetValue(node.Value); 是什么以及为什么它在编译时创建等等。我只对获取其字段值感兴趣。 我能想到的一件事是通过反思。有类似下面的东西,但它似乎并不整洁。

id

红利问题

在使用gettting VisitConstant值的代码时,我更改了DisplayClass方法,如下所示(虽然这不会解决我的问题)并得到一个例外,说“'object'不包含'id'“

的定义

enter image description here

奖金问题

由于动态在运行时得到解决,而dynamic是在编译时创建的,为什么我们不能使用var st = new {Id = 1L}; object o = st; dynamic dy = o; Assert.AreEqual(1L, dy.Id); 访问其字段?虽然下面的代码工作,我希望代码也可以工作。

{{1}}

2 个答案:

答案 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