【发布时间】:2011-12-17 15:07:48
【问题描述】:
当我使用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>>。我已经可以为非嵌套代表做到这一点。这在反射期间很有用,as discussed here。
我应该如何正确初始化这个Closure 对象,或者如何防止它出现?
【问题讨论】:
-
@JonSkeet:我会尽力而为,问题是整个示例非常复杂。我正在尝试递归调用以前编译的委托。当我尝试提取问题的一小部分时,here 你已经可以找到整个函数。
-
是的,缩短它肯定会有所帮助:)
-
@JonSkeet:作为更多信息,目标是允许创建包装函数,例如
Action<Action<SomeType>>可以被Action<Action<object>>包裹。这在 my previous implementation 上继续,只能对Action<SomeType>到Action<object>执行此操作。 -
你意识到这不是类型安全的,对吧?
Action<T>是逆变的,但Action<Action<T>>实际上是协变的。 -
是的,但我在反射期间使用它,我知道类型对应,但我不想知道具体类型,所以我可以支持任何类型。我在博客上写了in detail here。
标签: c# lambda expression-trees