2017-07-27 72 views
0

的ILGenerator調用實例方法我想構造一個泛型類型的實例並在該實例上調用一個方法。然後返回該方法的結果。具有參數

MethodInfo methodInfo = ...; 
... 
var genericType = typeof(GenericType<>).MakeGenericType(typeof(TOutput)); 
il.Emit(OpCodes.Newobj, genericType.GetConstructor(Type.EmptyTypes)); 
il.Emit(OpCodes.Ldobj, methodInfo); 
il.Emit(OpCodes.Callvirt, genericeClientHelper.GetMethod("MethodName", new Type[] { typeof(MethodInfo) })); 
il.Emit(OpCodes.Ret); 

我不斷收到'System.BadImageFormatException:'Bad class token。'異常。

GenericType類看起來像這樣

public class GenericType<T> 
{ 
    public T MethodName(MethodInfo methodInfo) 
    { 
     ... 
    } 
} 
+1

你能告訴我們更多的代碼嗎?比如,你如何構造這個類型,你如何定義這個方法?你當前的代碼看起來很好(雖然'genericeClientHelper.GetMethod'應該可能是'genericType.GetMethod',我相信。 – Rob

回答

1

你混淆了產生程序之間的邊界,產生程序。

具體而言,您的生成程序在運行時會構造一個對象的實例(MethodInfo實例),然後嘗試生成使用該實例的程序 - 它不能因爲該實例不存在於生成的程序,它存在於生成程序的內存中。

您必須在生成的程序中構建MethodInfo的實例 - 您必須編寫發射代碼來生成構建MethodInfo實例的IL。

什麼你正在試圖做的,使大約多大意義爲執行以下操作:

Person person = new Person("Antiduh", "United States"); 

var genericType = typeof(GenericType<>).MakeGenericType(typeof(TOutput)); 
il.Emit(OpCodes.Newobj, genericType.GetConstructor(Type.EmptyTypes)); 

// This doesn't make sense. The object referred to by 
// my `person` variable doesn't exist in the generated program. 
il.Emit(OpCodes.Ldobj, person); 

il.Emit(OpCodes.Callvirt, genericeClientHelper.GetMethod("MethodName", new Type[] { typeof(MethodInfo) })); 
il.Emit(OpCodes.Ret); 

這就是問題的編號爲1

問題2號是您使用了錯誤的操作碼時,試圖提供一個方法的參數 - Ldobj不會做你認爲它的作用。

而不是使用Ldobj,您必須通過任何方式加載參考,您可以通過修復代碼來創建內部methodInfo。它可能是本地的,所以你最終可能會使用Ldloc或其某種形式。

爲了得到完整的圈子,你得到錯誤「Bad class token」的原因是在編譯的IL中應該遵循Ldobj的值應該是類元數據令牌。你提供的不是一個類標記,因此是錯誤。

作爲一個演示,下面是一個模擬您正在嘗試執行的完整程序。

private static void BuildAssembly() 
{ 
    AssemblyName assemblyName; 
    AssemblyBuilder assmBuilder; 
    ModuleBuilder modBuilder; 

    assemblyName = new AssemblyName("Generated"); 

    // Note the save directory is the directory containing this project's solution file. 
    assmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
     assemblyName, 
     AssemblyBuilderAccess.RunAndSave, 
     Assembly.GetExecutingAssembly().Location + @"\..\..\..\.." 
    ); 

    modBuilder = assmBuilder.DefineDynamicModule(
     assemblyName.Name, 
     assemblyName.Name + ".dll", 
     true 
    ); 

    /* 
    * public class GenericsDemo { 
    * } 
    */ 
    TypeBuilder typeBuilder = modBuilder.DefineType(
     "Generated.GenericsDemo", 
      TypeAttributes.Public 
    ); 

    BuildCallListMethod(typeBuilder); 

    typeBuilder.CreateType(); 

    assmBuilder.Save(assemblyName.Name + ".dll"); 
} 

private static void BuildCallListMethod(TypeBuilder typeBuilder) 
{ 
    // public void CallList() { 
    //  List<object> list = new List<object>(); 
    //  object thing = new object(); 
    //  list.Add(thing); 
    // } 

    var listOfObject = typeof(List<object>); 
    var objType = typeof(object); 

    // public void CallList() { 
    var method = typeBuilder.DefineMethod( 
     "CallList", 
     MethodAttributes.Public | MethodAttributes.HideBySig, 
     CallingConventions.HasThis 
    ); 

    var gen = method.GetILGenerator(); 

    // List<int> list; 
    var listLocal = gen.DeclareLocal(listOfObject); 
    listLocal.SetLocalSymInfo("list"); 

    // object thing; 
    var thingLocal = gen.DeclareLocal(objType); 
    thingLocal.SetLocalSymInfo("thing"); 

    // list = new List<object>(); 
    gen.Emit(OpCodes.Newobj, listOfObject.GetConstructor(Type.EmptyTypes)); 
    gen.Emit(OpCodes.Stloc_0); 

    // thing = new object(); 
    gen.Emit(OpCodes.Newobj, objType.GetConstructor(Type.EmptyTypes)); 
    gen.Emit(OpCodes.Stloc_1); 

    // list.Add(thing); 
    gen.Emit(OpCodes.Ldloc_0); // loads `list`. 
    gen.Emit(OpCodes.Ldloc_1); // loads `thing`. 
    gen.EmitCall(OpCodes.Callvirt, listOfObject.GetMethod("Add"), null); 

    gen.Emit(OpCodes.Ret); 
} 
+0

感謝你的回答。我知道​​問題出在MethodInfo在錯誤的層次上,我希望有一種方法將其發送到生成的方法,這種方式可以在不撕毀我需要的部分數據的情況下工作。 – Mike