2017-02-24 22 views
1

我正嘗試使用Reflection.Emit創建靜態字段。如何使用Reflection.Emit創建靜態字段

但是,當我嘗試加載它時,我得到一個InvalidProgramException。

以下代碼重現了我的問題。

行:

generator.Emit(OpCodes.Ldsfld, builderField); 

導致以下異常:

Unhandled Exception: System.InvalidProgramException: Common Language Runtime detected an invalid program. 

沒有該行的程序運行正常,並返回 「測試」 如預期

 var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(
      new AssemblyName(Guid.NewGuid().ToString()), 
      AssemblyBuilderAccess.Run); 
     var module = assemblyBuilder.DefineDynamicModule("module1"); 

     var typeBuilder = module.DefineType("MyType", TypeAttributes.Public|TypeAttributes.Class); 
     typeBuilder.AddInterfaceImplementation(typeof(IMyType)); 

     var builderField = typeBuilder.DefineField("_builder", typeof(StringBuilder), FieldAttributes.Static | FieldAttributes.Private); 

     var methodBuilder = typeBuilder.DefineMethod(
      "UseStringBuilder", 
      MethodAttributes.Public | MethodAttributes.Virtual, 
      typeof(string), 
      new Type[0]); 

     var generator = methodBuilder.GetILGenerator(); 

     //this line causes InvalidProgramException, without this line it works 
     generator.Emit(OpCodes.Ldsfld, builderField); 

     generator.Emit(OpCodes.Ldstr, "Test"); 
     generator.Emit(OpCodes.Ret); 


     var typeInfo = typeBuilder.CreateTypeInfo(); 
     var myType = typeInfo.AsType(); 

     IMyType instance = (IMyType)Activator.CreateInstance(myType); 
     instance.UseStringBuilder(); 

我知道我沒有將StringBuilder用於任何將它放在堆棧上的東西,並且它將是空的,因爲它是我的沒有初始化,但是它是一個簡化版本(用於重現問題),它是一個懶惰初始化字段的較大程序,所以我需要將它加載到堆棧中,以便我可以檢查它是否爲null。然而,在我能做任何事情之前,ldsfld指令會崩潰程序。

ILSPY此代碼的工作版本是這樣的:

// Fields 
.field private static class [System.Runtime]System.Text.StringBuilder _builder 

// Methods 
.method public final hidebysig newslot virtual 
    instance string UseStringBuilder() cil managed 
{ 
    // Method begins at RVA 0x20ee 
    // Code size 28 (0x1c) 
    .maxstack 8 

    IL_0000: ldsfld class [System.Runtime]System.Text.StringBuilder JsonicsTests.MyType::_builder 
    IL_0005: brtrue.s IL_0011 

    IL_0007: newobj instance void [System.Runtime]System.Text.StringBuilder::.ctor() 
    IL_000c: stsfld class [System.Runtime]System.Text.StringBuilder JsonicsTests.MyType::_builder 

    IL_0011: ldsfld class [System.Runtime]System.Text.StringBuilder JsonicsTests.MyType::_builder 
    IL_0016: callvirt instance string [System.Runtime]System.Object::ToString() 
    IL_001b: ret 
} 

什麼是創建和使用Reflection.Emit的使用靜態字段的正確方法是什麼?

我正在Linux上運行.net core 1.1,如果這是相關的。

回答

1

你有一個不平衡堆棧的問題:你在函數結束後在堆棧上留下一個值。試試:

generator.Emit(OpCodes.Ldsfld, builderField); 
generator.Emit(OpCodes.Pop); // Remove the value from the stack 
+0

修復它。我知道我缺少一些基本的東西。我的問題現在似乎很低,我應該刪除它嗎? – trampster

+1

@trampster我認爲你的問題非常有趣。堆棧必須爲空/必須具有單個值(如果您需要返回它)在生成的函數結尾處的事實是非常重要的,但在關於生成代碼的教程中寫得不夠強烈。 – xanatos