DLR LambdaExpressions and the System.Runtime.CompilerService

本文关键字:Runtime CompilerService System the LambdaExpressions and DLR | 更新日期: 2023-09-27 17:58:26

我正在为Microsoft DLR开发一种小型编程语言,在调用匿名方法时遇到了一些问题。具体来说,代码:

Delegate CompiledBody = Expression.Lambda(rt.Parser.ParseSingle(Body), parms).Compile();

因此,parms是一个包含单个ParameterExpression的数组,第一个参数包含用于定义匿名函数的适当表达式。当我尝试在CompiledBody.Method(MethodInfo)上使用Expression.Call调用我的Delegate时,我收到错误:

Unhandled Exception: System.ArgumentException: Expression of type 'System.Object' 
cannot be used for parameter of type 'System.Runtime.CompilerServices.Closure' 
of method 'Shiro.Runtime.ShiroAtom lambda_method(System.Runtime.CompilerServices
.Closure, Shiro.Runtime.ShiroAtom)'

现在,在这个过程中,我的一个参数方法获得了第二个参数,类型为System.Runtime.CompilerServices.Closure(第二个类型为ShiroAtom,是我的参数)。这是有道理的,除了(a)我真的不在乎这个上下文中的方法是否在闭包范围内,以及(b)我似乎甚至不能创建一个空的闭包范围来传递这个参数。

如果有任何帮助,我将不胜感激!提前谢谢。

编辑:一些基于以下令人敬畏的回复的额外信息:

这个代码发生在我的Parser的深处。我有一个令牌流(实际上是Atoms),它们被翻译成AST。这个特定的位是函数调用解析例程。它创建了一个CompiledBody,然后尝试使用以下内容调用它:

return Expression.Call(CompiledBody.Method, Expression.Constant("argument"));

生成的Lambda表示一个函数。根据我的体系结构,只有少数地方可以调用DynamicInvoke或仅调用Compiled Delegate,但这不是其中之一。我希望我能提供一个更实质性的例子,但这种情况发生在手工编码的解析器中,需要太多的代码才能真正传达为什么会出现这种情况,但我确实需要一种通过Expression.call调用编译后的Lambda的方法,如上所示。

问题的关键是,我的Compiled Lambda在我指定的参数之外还需要一个额外的参数,CompilerServices.Closure,而我不知道如何创建一个。

DLR LambdaExpressions and the System.Runtime.CompilerService

如果你能共享你正在编译的主体,因为它将包含实际的闭包以及你如何调用它,那将是很有帮助的。我的猜测是,你试图以某种方式"手动"调用生成的委托,而不是抓住委托对象的某个部分并简单地生成invoke表达式。如果你想使用DLR闭包,它应该是这样的:

using System;
using System.Linq.Expressions;
class Program {
    static void Main(string[] args) {
        var outerParam = Expression.Parameter(typeof(int), "outerParam");
        var lambda =
            Expression.Lambda<Func<int, Action>>(
                Expression.Lambda<Action>(
                    Expression.Call(
                        typeof(Console).GetMethod("WriteLine", new Type[] { typeof(object) }),
                        Expression.Convert(outerParam, typeof(object))
                    )
                ),
                outerParam
            ).Compile();
        var actionParam = Expression.Parameter(typeof(Action), "action");
        var lambdaInvoker =
            Expression.Lambda<Action<Action>>(
                Expression.Invoke(actionParam),
                actionParam
            ).Compile();
        lambdaInvoker(lambda(100));
        lambdaInvoker(lambda(200));
        Console.ReadLine();
    }
}

这创建了3个lambda:第一个lambda包含第二个内部lambda,它闭合在一个参数上。生成的闭包委托的类型是在创建lambda表达式时指定的类型,即使其中有一个额外的隐藏参数。第三个lambda显示了如何从另一个lambda调用它,即通过委托调用。最后,我们将代表联系在一起,展示其工作原理。

还有一件事需要注意,由于CLR的限制,DLR闭包现在实际上并没有那么好地执行。创建闭包实际上是一个相当缓慢的过程,因为它需要经过反射,而不是能够直接创建委托。如果您关心委托的性能,您将希望通过自己的数据结构来跟踪中的变量和封闭值流(这是IronRuby和IronPython所做的)。