我正在为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)我真的不关心这个上下文中的方法是否在Closure范围内,(b)我似乎无法创建一个空的Closure范围来传递这个参数。
我很感激任何帮助!提前谢谢。
编辑:基于以下精彩回复的一些其他信息:
此代码出现的位置在我的Parser的内部深处。我有一个令牌流(实际上是Atoms),它被翻译成AST。这个特定的位是函数调用解析例程。它创建了一个CompiledBody,然后尝试使用类似的东西来调用它:
return Expression.Call(CompiledBody.Method, Expression.Constant("argument"));
生成的Lambda代表一个函数。基于我的架构,我只能调用DynamicInvoke或只调用Compiled Delegate,这不是其中之一。我希望我能提供一个更实际的例子,但这种情况发生在一个手动编码的解析器中,并且需要太多的代码才能真正地传达为什么情况就是这样,但我确实需要一种通过Expression.Call调用已编译的Lambda的方法,如上所示。
问题的症结在于我的Compiled Lambda需要为我指定的参数添加一个额外的参数,一个CompilerServices.Closure,而且我不知道如何制作一个参数。
答案 0 :(得分:3)
如果您可以共享正在编译的主体,那将会有所帮助,因为它将包含实际的闭包以及您如何调用它。我的猜测是你试图以某种方式“手动”调用结果委托,而不是保留委托对象的某些东西,只是生成一个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:第1个包含第2个内部lambda,它关闭参数。生成的闭包委托的类型是创建lambda表达式时指定的类型,即使那里有一个额外的隐藏参数。第3个lambda显示了如何从另一个lambda调用它 - 即通过委托调用。最后,我们将代表们联系在一起,展示它是如何运作的。
还有一点需要注意的是,由于CLR的限制,DLR关闭现在实际上并没有那么好。创建闭包实际上是一个非常缓慢的过程,因为它需要通过反射而不是直接创建委托。如果您担心委托的性能,那么您将需要通过自己的数据结构跟踪变量并关闭值(这就是IronRuby和IronPython所做的事情)。