Linq 编译带有表达式.Lambda()参数的委托超出范围,但它真的是吗?

Linq 编译带有表达式.Lambda()参数的委托超出范围,但它真的是吗?,linq,lambda,compilation,invalidoperationexception,Linq,Lambda,Compilation,Invalidoperationexception,今天我在动态表达式构建库中实现一个特性时遇到了一个有趣的问题。更具体地说,是一种定义表达式中运算符优先级的功能,但并不相关 当LINQ引擎编译最终表达式时,我遇到一个invalidoOperationException声明Lambda参数超出范围 分配相关的参数expression对象后,问题就会显现出来 使用一个完整且格式良好的lambda表达式树,我发现在编译lambda时,将lambda的参数expression对象重新指定给有效引用是无效的 这是我在应用修复之前最初采用的行为的简短描述:

今天我在动态表达式构建库中实现一个特性时遇到了一个有趣的问题。更具体地说,是一种定义表达式中运算符优先级的功能,但并不相关

当LINQ引擎编译最终表达式时,我遇到一个
invalidoOperationException
声明
Lambda参数超出范围

分配相关的
参数expression
对象后,问题就会显现出来

使用一个完整且格式良好的lambda表达式树,我发现在编译lambda时,将lambda的
参数expression
对象重新指定给有效引用是无效的

这是我在应用修复之前最初采用的行为的简短描述:

  • 构建表达式树,用于
    Queryable.Where
    ,根表达式为
    LambdaExpression
    ,使用
    expression.Lambda(表达式,表达式.参数(GetType(type),“name”)
  • 访问表达式树(使用LinqKit),构建所遇到参数的哈希表
  • 相同名称的后续参数将替换为遇到的相同名称的第一个参数
结果是一个表达式树,其中所有同名的
ParameterExpression
引用都指向同一个对象-但编译时遇到了
InvalidOperationException

我应用的修复采用了以下行为:

  • 将参数构建为
    ParameterExpression
  • 使用
    Expression.Lambda(表达式,参数数组)
  • 访问表达式树(使用LinqKit),用
    parameterArray
最终结果编译得很好,即使Lambda表达式结构在概念上与前一行为的输出相同

问题是:为什么第一个失败了,第二个成功了

下面是一个要复制的测试夹具类(请原谅vb),包括测试用例和几个支持类(取决于nUnit、LinqKit):

注意:缺少TestFixture和测试属性声明-如何在标记中执行


AFIK表达式树不考虑用相同参数创建的两个参数表达式对象,即“同一个参数”。 在没有测试代码的情况下,这就是最突出的:当我阅读第一个(失败的)场景时,您使用遇到的第一个这样的参数替换所有相同的命名参数,但是第一个遇到的参数与您在对Expression.Lambda()的调用中创建的参数Expression对象不同。在第二个(后续)场景中,它是

编辑我应该补充一点,我没有使用LinqKit的ExpressionVisitor,但据我所知,它基于我使用过的代码,其中VisitLambda不是很健壮:

    protected virtual Expression VisitLambda(LambdaExpression lambda)
    {
        Expression body = this.Visit(lambda.Body);
        if (body != lambda.Body)
        {
            return Expression.Lambda(lambda.Type, body, lambda.Parameters);
        }
        return lambda;
    }

请注意,会访问表达式的主体,但不会访问其参数。如果LinqKit没有改进这一点,那将是失败的地方。

事实上,在成功的场景中,情况确实如此——创建lambda表达式时使用的参数是用于替换表达式中所有匹配参数elsehare的参数,它会编译。在第二次访问表达式树时,我验证了第一个场景中的参数表达式都指向预期的引用(包括lambda的参数):从概念上讲,表达式树是相同的。这几乎是因为lambda表达式保留了对其原始参数的内部引用。因此,在上面的使用中,LinqKit的ExpressionVisitor正确地访问并替换了lambda的参数,而不仅仅是其主体中引用的参数?事实上,经过反思,我还没有验证这一点。但我会。。。如果是这种情况,请通过扩展来纠正该行为,然后重试。我明天会回来报告:)事实上,我刚查过来源,是的!你说得对!很好的发现,谢谢你的输入!
    protected virtual Expression VisitLambda(LambdaExpression lambda)
    {
        Expression body = this.Visit(lambda.Body);
        if (body != lambda.Body)
        {
            return Expression.Lambda(lambda.Type, body, lambda.Parameters);
        }
        return lambda;
    }