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

3

这是对此处提供的解决方案的扩展。我创建了一个静态方法,返回一个对象。我的目标是为我在运行时定义的类型编写一个动态方法,使其返回这个静态方法返回的对象。到目前为止,我的代码如下:

 // 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。
2个回答

4
至少有一个问题是,您正在将“this”引用(OpCodes.Ldarg_0)推送到堆栈上,即使它从未弹出(因为您正在调用静态方法)。 我建议删除该行并查看它是否表现更好。
另一个问题是,您正在向EmitCall方法传入new Type[] { returnType }。那是可选参数(params)的意图,我怀疑您的方法实际上没有任何参数。 因此,您应该删除该参数。 编辑: 根据评论,您正在尝试将静态已知的System.Type对象传递给您动态调用的方法。 这是可能的,但您需要跳过一些障碍。
  1. Get a reference to the MethodInfo for the method Type.GetTypeFromHandle:

    MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
    
  2. Use the following lines of IL to push your returnType onto the stack:

    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. 从方法返回。


因此,删除load arg 0 emit可以使其调用静态方法,但是我确实想将returnType变量传递给静态方法,但似乎它正在传递'this'。当我调试静态方法时,类型为'Type'的参数不是returnType的值,而是我创建的动态类的类型。 - OnResolve
请确认您的方法签名是否像这样:object GetStoredObject(Type returnType);。如果是这样,那么您确实需要传入一个参数。但是,您实际上要传递什么类型的参数? - Kirk Woll
"public static Object GetStoredObject(Type contract)"。我的上述方法名为EmitReferenceMethodBody(Type returnType),它会传递我想要传递给静态方法的类型。 - OnResolve
可以运行了!非常感谢。你的解释非常直观易懂! - OnResolve

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).MakeGenericType(returnType)替换typeof(ObjectStore)。如果GetStoredObject函数本身需要泛型参数,则将Expression.Call语句中的null更改为new Type[] { returnType }。如果每个传递的类型都生成一次,并且您计划经常使用它,则将这些Func缓存到Dictionary<Type, Func<Type, object>>中,并仅构建一次(因为重复编译表达式会浪费系统资源)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接