2017-02-24 9 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

関連する問題