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。

C#从IL中的静态方法调用并返回一个对象

至少有一个问题是,您正在将"this"引用(OpCodes.Ldarg_0)推送到堆栈上,即使它从未弹出(因为您正在调用静态方法)。我会试着去掉那条线,看看它是否表现得更好。

另一个问题是将new Type[] { returnType }传递给EmitCall方法。这是针对可选参数(params)的,我怀疑您的方法实际上没有任何参数。因此,您也应该删除该论点。

编辑:

基于注释,您正试图将静态已知的System.Type对象传递给正在动态调用的方法。这是可能的,但你需要跳过几个环节。

  1. 获取方法Type.GetTypeFromHandle:对MethodInfo的引用

    MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
    
  2. 使用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);

此代码的过渡堆栈行为为:

  1. 使用Opcodes.Ldtoken将指定的Type引用对应的RuntimeTypeHandle推送到堆栈上。

  2. 调用getTypeFromHandle,它将类型句柄从堆栈中弹出,并将实际的System.Type推送到堆栈中。

  3. 调用您的静态方法,它将从堆栈中弹出Type参数,并将您自己的方法的返回值推送到堆栈中。

  4. 从方法返回。

表达式树可能是更好的解决方案。通过表达式使用动态类型创建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>>中并只构建一次可能是个好主意(因为重复编译表达式会浪费系统资源)。