编译lambda表达式会产生带有Closure参数的委托
本文关键字:参数 Closure 表达式 lambda 编译 | 更新日期: 2023-09-27 18:12:38
当我使用Expression.Lambda( ... ).Compile()
来从表达式树创建一个委托时,结果是一个委托的第一个参数是Closure
。
public static Func<T, T, T> CreateTest<T>()
{
ParameterExpression a = Expression.Parameter( typeof( T ) );
ParameterExpression b = Expression.Parameter( typeof( T ) );
Expression addition = Expression.Add( a, b );
return (Func<T, T, T>)Expression.Lambda( addition, a, b ).Compile();
}
...
// 'addition' equals
// Int32 lambda_method(
// System.Runtime.CompilerServices.Closure,
// Int32,
// Int32 )
Func<int, int, int> addition = DelegateHelper.CreateTest<int>();
int result = addition( 5, 5 );
我可以很容易地通过普通代码调用委托而不传递Closure
对象,但是这个Closure
来自哪里?
我如何动态地调用这个委托?
// The following does not work.
// Exception: MethodInfo must be a runtime MethodInfo object.
MethodInfo additionMethod = addition.Method;
int result = (int)additionMethod.Invoke( null, new object[] { 5, 5 } );
使用表达式树,看起来我必须传递Closure
对象。
PropertyInfo methodProperty
= typeof( Delegate ).GetProperty( "Method", typeof( MethodInfo ) );
MemberExpression getDelegateMethod
= Expression.Property( Expression.Constant( addition ), methodProperty );
Func<MethodInfo> getMethodInfo
= (Func<MethodInfo>)Expression.Lambda( getDelegateMethod ).Compile();
// Incorrect number of arguments supplied for call to method
// 'Int32 lambda_method(System.Runtime.CompilerServices.Closure, Int32, Int32)'
Expression call
= Expression.Call(
getMethodInfo(),
Expression.Constant( 5 ), Expression.Constant( 5 ) );
这是一个简单的例子,它本身没有意义。我实际上想要实现的是能够将Func<Action<SomeObject>>
与Func<Action<object>>
包装起来。对于非嵌套委托,我已经可以这样做了。这在反射期间很有用,就像这里讨论的那样。
我应该如何正确地初始化这个Closure
对象,或者我如何防止它在那里?
您看到的Closure
类型是一个实现细节。MSDN对此非常明确:
这个API支持。net框架基础设施,但不支持旨在直接从您的代码中使用。表示运行时。动态生成方法的状态。
表达式树可以有状态
Closure实例将包含lambda表达式关闭的所有非文字常量。它还可以包含表达式树中嵌套lambda函数的委托链。
为了实现这一点,表达式树编译器使用了一个可爱的小技巧。它使用DynamicMethod
在内存中生成代码,根据定义,这是静态的。然而,他们正在创建一个"在其第一个参数上关闭"的委托。意思是CLR会传递委托的目标字段作为静态方法的第一个参数,所以你不需要。有效地隐藏了Closure参数
你的问题的解决方案很简单,不要尝试调用方法,调用委托,或者通过使用委托。使用反射或表达式时使用DynamicInvoke。在表达式树的上下文中调用