C#从IL中的静态方法调用并返回一个对象
本文关键字:调用 返回 一个对象 静态方法 IL | 更新日期: 2023-09-27 18:27:07
这是对此处提供的解决方案的扩展。我创建了一个静态方法,该方法返回一个对象。我的目标是为我在运行时定义的类型编写一个动态方法,以返回该静态方法正在返回的对象。到目前为止我的代码:
// 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。
至少有一个问题是,您正在将"this"引用(OpCodes.Ldarg_0
)推送到堆栈上,即使它从未弹出(因为您正在调用静态方法)。我会试着去掉那条线,看看它是否表现得更好。
另一个问题是将new Type[] { returnType }
传递给EmitCall
方法。这是针对可选参数(params
)的,我怀疑您的方法实际上没有任何参数。因此,您也应该删除该论点。
编辑:
基于注释,您正试图将静态已知的System.Type
对象传递给正在动态调用的方法。这是可能的,但你需要跳过几个环节。
-
获取方法
Type.GetTypeFromHandle
:对MethodInfo
的引用MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
-
使用IL的以下行将
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);
此代码的过渡堆栈行为为:
使用
Opcodes.Ldtoken
将指定的Type
引用对应的RuntimeTypeHandle
推送到堆栈上。调用
getTypeFromHandle
,它将类型句柄从堆栈中弹出,并将实际的System.Type
推送到堆栈中。调用您的静态方法,它将从堆栈中弹出
Type
参数,并将您自己的方法的返回值推送到堆栈中。从方法返回。
表达式树可能是更好的解决方案。通过表达式使用动态类型创建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).MakeGenericType(returnType)
替换typeof(ObjectStore)
。如果GetStoredObject
函数本身需要泛型参数,那么您可以将Expression.Call
语句中的null
更改为new Type[] { returnType }
。如果它为您传入的每个类型生成一次,并且您计划大量使用它,那么将这些Func缓存到Dictionary<Type, Func<Type, object>>
中并只构建一次可能是个好主意(因为重复编译表达式会浪费系统资源)。