簡體   English   中英

C#從IL中的靜態方法調用並返回對象

[英]C# Call and return an object from a static method in IL

這是對此處提供的解決方案的擴展。 我創建了一個靜態方法,該方法向我返回了一個對象。 我的目標是為我在運行時定義的類型編寫動態方法,以向我返回此靜態方法返回的對象。 到目前為止,我的代碼:

 // 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。

至少一個問題是,即使從未彈出“此”引用( OpCodes.Ldarg_0 ),它也從未被彈出(因為您正在調用靜態方法)。 我會嘗試刪除該行,看看它的表現是否更好。

另一個問題是您EmitCall new Type[] { returnType }傳遞給EmitCall方法。 這是用於可選參數( params )的,我懷疑您的方法實際上沒有任何參數。 因此,您也應該刪除該參數。

編輯:

基於注釋,您嘗試將靜態已知的System.Type對象傳遞給要動態調用的方法。 這是可能的,但是您需要跳幾圈。

  1. 獲取對方法Type.GetTypeFromHandleMethodInfo的引用:

     MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle"); 
  2. 使用以下IL returnType您的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)替換為typeof(ObjectStore).MakeGenericType(returnType) 如果GetStoredObject函數本身需要通用參數,則可以將Expression.Call語句中的null更改為new Type[] { returnType } 如果為您傳入的每種類型生成一次該代碼,並且打算大量使用,則最好將這些Funcs緩存到Dictionary<Type, Func<Type, object>>並僅構建一次(因為反復編譯表達式會浪費系統資源。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM