编译lambda表达式会导致委托使用Closure参数

时间:2011-10-28 21:54:57

标签: c# lambda expression-trees

当我使用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。我已经可以为非嵌套委托执行此操作。这在反射期间很有用as discussed here

我应该如何正确初始化此{{1}}对象,或者如何防止它出现?

1 个答案:

答案 0 :(得分:12)

您看到的Closure类型是一个实现细节。 MSDN非常明确:

  

此API支持.NET Framework基础结构,但不支持   旨在直接从您的代码中使用。表示运行时   动态生成方法的状态。

表达式树可以具有状态。

Closure实例将包含lambda表达式关闭的所有非文字常量。它还可以包含表达式树中嵌套lambda的委托链。

为了实现这一点,表达式树编译器使用了一个可爱的小技巧。它使用DynamicMethod在内存代码中生成,根据定义静态。然而,他们正在创建一个“closed over its first argument”的代表。这意味着CLR将传递委托的目标字段作为静态方法的第一个参数,因此您不必这样做。有效地隐藏了你的Closure论点。

您的问题的解决方案很简单,不要尝试调用方法,调用代理,或者在使用反射时使用Delegate.DynamicInvoke,或者在使用反射时使用Expression.Invoke。表达树。