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。

4

2 回答 2

4

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

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

编辑:

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

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

    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. 使用 .将RuntimeTypeHandle对应于指定Type引用的内容压入堆栈Opcodes.Ldtoken

  2. InvokegetTypeFromHandle将类型句柄从堆栈中弹出,并将实际System.Type压入堆栈。

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

  4. 从方法返回。

于 2012-03-08T19:46:22.093 回答
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中的更改为. 如果这为您传入的每种类型生成一次并且您计划大量使用它,那么将这些 Funcs 缓存到 a 中并且只构建一次可能是一个好主意(因为重复编译 Expressions 会浪费系统资源) .Expression.Callnew Type[] { returnType }Dictionary<Type, Func<Type, object>>

于 2012-03-08T19:59:09.613 回答