我正在尝试通过TypeBuilder在运行时生成一个类型。我正在使用MethodBuilder生成该类型的实例方法,但是我不希望通过IlGenerator.Emit
生成il;相反,我想创建一个表示方法的表达式,所以我可以将它转换为MethodBuilder的实例方法。
这可能吗?如果是这样,我如何将Expression转换为MethodBuilder实例方法?
答案 0 :(得分:10)
是的,你可以,但你必须做一些额外的工作。跳转到代码的代码段即可。
不直接,不。正如SO Question: LambdaExpression CompileToMethod中所述,虽然.NET 4.0 中的LambdaExpression.CompileToMethod
采用MethodBuilder
,但它只能表示静态方法。
那么,您需要首先创建静态方法引用来解决此限制,然后创建一个调用该静态方法的实例方法。如果表达式中没有任何“活动对象”(即,在创建表达式时使用现有对象引用),则创建静态方法然后创建调用静态方法的实例方法非常简单。但是,如果表达式中有“活动对象”,CompileToMethod
将通知您它无法使用表达式,因为表达式中有活动对象。
您可以改为向生成的类型添加委托字段,然后从实例方法调用委托字段并将方法参数转发给委托,而不是创建静态方法。
码假设TypeBuilder
名为_typeBuilder
,MethodBuilder
名为methodBuilder
,代表名称为delegateToInvoke
:
// create a field to hold the dynamic delegate
var fieldBuilder = _typeBuilder.DefineField(
"<>delegate_field",
delegateToInvoke.GetType(),
FieldAttributes.Private);
// remember to set it later when we create a new instance
_fieldsToSet.Add(new KeyValuePair<FieldInfo, object>(fieldBuilder, delegateToInvoke));
var il = methodBuilder.GetILGenerator();
// push the delegate onto the stack
il.Emit(OpCodes.Ldarg_0);
// by loading the field
il.Emit(OpCodes.Ldfld, fieldBuilder);
// if the delegate has a target, that means the first argument is really a pointer to a "this"
// object/closure, and we don't want to forward it. Thus, we skip it and continue as if it
// wasn't there.
if (delegateToInvoke.Target != null)
{
parameters = parameters.Skip(1).ToArray();
}
// push each argument onto the stack (thus "forwarding" the arguments to the delegate).
for (int i = 0; i < parameters.Length; i++)
{
il.Emit(OpCodes.Ldarg, i + 1);
}
// call the delegate and return
il.Emit(OpCodes.Callvirt, delegateToInvoke.GetType().GetMethod("Invoke"));
il.Emit(OpCodes.Ret);
创建新实例时,请务必在使用实例之前设置字段:
generatedType.GetField("<>delegate_field", BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(instance, delegateToInvoke);