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

时间:2012-03-08 19:43:46

标签: c# reflection reflection.emit intermediate-language

这是对提供的解决方案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。

2 个答案:

答案 0 :(得分:4)

至少有一个问题是你正在将“this”引用(OpCodes.Ldarg_0)推入堆栈,即使它从未弹出(因为你正在调用 static 方法)。我会尝试删除该行,看看它是否表现得更好。

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

修改

根据评论,您尝试将静态已知的System.Type对象传递给您正在动态调用的方法。这是可能的,但你需要跳过几个圈。

  1. 获取方法MethodInfo的{​​{1}}的参考:

    Type.GetTypeFromHandle
  2. 使用以下IL行将MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle"); 推入堆栈:

    returnType
  3. 总之,您的代码应如下所示:

    gen.Emit(OpCodes.Ldtoken, returnType);
    gen.Emit(OpCodes.Call, getTypeFromHandle);
    

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

    1. 使用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推送到堆栈。

    2. 调用Opcodes.Ldtoken弹出堆栈类型句柄,并将实际的getTypeFromHandle推送到堆栈。

    3. 调用静态方法,该方法会将System.Type参数从堆栈中弹出,并将您自己方法的返回值压入堆栈。

    4. 从方法返回。

答案 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>>并且只构建一次可能是个好主意(因为重复编译表达式是一个浪费系统资源)。