这是对提供的解决方案here的扩展。我创建了一个静态方法,它返回一个对象。我的目标是为运行时定义的类型编写动态方法,以返回此静态方法返回的对象。到目前为止我的代码:
// type builder and other prep stuff removed for sake of space and reading
private void EmitReferenceMethodBody(Type returnType)
{
MethodBuilder builder =
typeBuilder.DefineMethod(
method.Name,
MethodAttributes.Virtual | MethodAttributes.Public,
method.CallingConvention,
method.ReturnType,
typeArray1);
builder.InitLocals = true;
ILGenerator gen = builder.GetILGenerator();
MethodInfo getStoredObject = typeof(ObjectStore).GetMethod("GetStoredObject", BindingFlags.Public | BindingFlags.Static);
MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
gen.Emit(OpCodes.Ldtoken, returnType);
gen.Emit(OpCodes.Call, getTypeFromHandle);
gen.Emit(OpCodes.Call, getStoredObject);
gen.Emit(OpCodes.Ret);
}
更新的代码现在调用该方法,但似乎传递了动态创建的类型的类型,而不是变量returnType。
答案 0 :(得分:4)
至少有一个问题是你正在将“this”引用(OpCodes.Ldarg_0
)推入堆栈,即使它从未弹出(因为你正在调用 static 方法)。我会尝试删除该行,看看它是否表现得更好。
另一个问题是您将new Type[] { returnType }
传递给EmitCall
方法。这是用于可选参数(params
),我怀疑你的方法实际上没有任何参数。因此,您也应该删除该参数。
修改强>
根据评论,您尝试将静态已知的System.Type
对象传递给您正在动态调用的方法。这是可能的,但你需要跳过几个圈。
获取方法MethodInfo
的{{1}}的参考:
Type.GetTypeFromHandle
使用以下IL行将MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
推入堆栈:
returnType
总之,您的代码应如下所示:
gen.Emit(OpCodes.Ldtoken, returnType);
gen.Emit(OpCodes.Call, getTypeFromHandle);
此代码的过渡堆栈行为是:
使用MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
gen.Emit(OpCodes.Ldtoken, returnType);
gen.Emit(OpCodes.Call, getTypeFromHandle);
gen.EmitCall(OpCodes.Call, getStoredObject);
gen.Emit(OpCodes.Ret);
将对应于指定RuntimeTypeHandle
引用的Type
推送到堆栈。
调用Opcodes.Ldtoken
弹出堆栈类型句柄,并将实际的getTypeFromHandle
推送到堆栈。
调用静态方法,该方法会将System.Type
参数从堆栈中弹出,并将您自己方法的返回值压入堆栈。
从方法返回。
答案 1 :(得分:1)
表达式树可能是更好的解决方案。使用表达式动态输入创建Func<Type, object>
非常容易。
ParameterExpression paramEx = Expression.Parameter(typeof(Type), "paramObject");
Expression callExpression = Expression.Call(typeof(ObjectStore), "GetStoredObject", null, paramEx);
Expression<Func<Type, object>> funcExpression = Expression.Lambda<Func<Type, object>>(callExpression, paramEx);
Func<Type, object> actualFunc = funcExpression.Compile();
现在,如果ObjectStore需要通用参数,您可以将typeof(ObjectStore)
替换为typeof(ObjectStore).MakeGenericType(returnType)
。如果GetStoredObject
函数本身需要通用参数,那么您可以将null
语句中的Expression.Call
更改为new Type[] { returnType }
。如果为您传入的每种类型生成一次并且计划大量使用它,那么将这些Func缓存到Dictionary<Type, Func<Type, object>>
并且只构建一次可能是个好主意(因为重复编译表达式是一个浪费系统资源)。