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

标签 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 -> Search -> VisitConstant

在此执行路径中常量表达式(1L)传递给Search method 是一个实常数值。到这里,一切正常,assert如期成功。当VisitConstant通过第一个路径调用 node.Value.GetType()Int64及其 .Value1L .

2. TestParam -> GetStudent -> Search -> VisitConstant

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

问题

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

问题

我怎样才能得到 id第二条路径的值(value)?我知道闭包,多好的DisplayClass是以及为什么它是在编译时创建的等等。我只对获取它的字段值感兴趣。 我能想到的一件事是,通过反射(reflection)。像下面这样的东西,但它看起来并不整洁。

node.Value.GetType().GetFields()[0].GetValue(node.Value);

奖金问题

在玩弄获取 id 的代码时我更改的值 VisitConstant像下面这样的方法(虽然它不会解决我的问题)并得到一个异常说 “'object' does not contain a definition for 'id'”

enter image description here

奖励问题

由于动态在运行时得到解决并且 DisplayClass是在编译时创建的,为什么我们不能用 dynamic 访问它的字段? ?虽然下面的代码有效,但我希望该代码也能正常工作。

var st = new {Id = 1L};
object o = st;
dynamic dy = o;
Assert.AreEqual(1L, dy.Id);

最佳答案

VisitConstant 在这里无济于事,因为它接收编译器构造的 ConstantExpression,它使用私有(private)匿名类的对象来存储值 lambda 已关闭(DisplayClassxxx )

相反,我们应该重写 VisitMember 方法并检查它的 MemberExpression,它已经将 ConstantExpression 作为内部 Expression .

这是工作测试,几乎没有反射(reflection)。

[TestClass]
public class UnitTest2
{
    [TestMethod]
    public void TestMethod2()
    {
        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 MyExpressionVisitor2();
        visitor.Visit(filter.Body);
    }
}

//ExpressionVisitor
public class MyExpressionVisitor2 : ExpressionVisitor
{
    protected override Expression VisitMember(MemberExpression node)
    {
        switch (node.Expression.NodeType)
        {
            case ExpressionType.Constant:
            case ExpressionType.MemberAccess:
            {
                var cleanNode = GetMemberConstant(node);

                //Test
                Assert.AreEqual(1L, cleanNode.Value);

                return cleanNode;
            }
            default:
            {
                return base.VisitMember(node);
            }
        }
    }


    private static ConstantExpression GetMemberConstant(MemberExpression node)
    {
        object value;

        if (node.Member.MemberType == MemberTypes.Field)
        {
            value = GetFieldValue(node);
        }
        else if (node.Member.MemberType == MemberTypes.Property)
        {
            value = GetPropertyValue(node);
        }
        else
        {
            throw new NotSupportedException();
        }

        return Expression.Constant(value, node.Type);
    }
    private static object GetFieldValue(MemberExpression node)
    {
        var fieldInfo = (FieldInfo)node.Member;

        var instance = (node.Expression == null) ? null : TryEvaluate(node.Expression).Value;

        return fieldInfo.GetValue(instance);
    }

    private static object GetPropertyValue(MemberExpression node)
    {
        var propertyInfo = (PropertyInfo)node.Member;

        var instance = (node.Expression == null) ? null : TryEvaluate(node.Expression).Value;

        return propertyInfo.GetValue(instance, null);
    }

    private static ConstantExpression TryEvaluate(Expression expression)
    {

        if (expression.NodeType == ExpressionType.Constant)
        {
            return (ConstantExpression)expression;
        }
        throw new NotSupportedException();

    }
}

关于c# - 由于关闭而将实际值包装到 DisplayClass 中时获取 ConstantExpression.Value,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25705339/

相关文章:

python - 将 memoization 装饰器理解为闭包

javascript - 重构 jquery 闭包 : How to refactor to nominal function?

c# - 将类型动态传递给 <T>

c# - 插入语句组合逻辑

php - 在创建动态菜单时需要帮助

ios - Storyboard上 UITableViewController 的动态原型(prototype)单元格高度大小

javascript - 如何用JS生成HTML让Google检测

ios - 如何获得一些闭包的总和

c# - 如何将 ControlTemplate 转换为 WPF 中的 XAML 字符串?

c# - 绑定(bind)不正确更新 usercontrol 属性 MVVM