2017-02-24 1 views
1

J'essaie de créer un champ statique à l'aide de Reflection.Emit.Comment créer un champ statique à l'aide de Reflection.Emit

Cependant, j'obtiens une exception InvalidProgramException lorsque j'essaie de le charger.

Le code suivant reproduit mon problème.

La ligne:

generator.Emit(OpCodes.Ldsfld, builderField); 

provoque l'exception suivante:

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

sans cette ligne le programme fonctionne très bien et retourne "Test" comme prévu

 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(); 

Je sais que je n'utilise pas le StringBuilder pour quelque chose juste en le mettant sur la pile, et qu'il sera nul comme il s non initialisé, cependant c'est une version simplifiée (dans le but de reproduire le problème) d'un programme plus grand qui paresseux initialise le champ, donc j'ai besoin de le charger sur la pile pour que je puisse le vérifier pour null. Cependant, l'instruction ldsfld bloque le programme avant que je puisse faire cela.

ILSPY pour une version de travail de ce code ressemble à ceci:

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

Quelle est la bonne façon de créer et d'utiliser un champ statique à l'aide Reflection.Emit? J'utilise .net core 1.1 sur Linux si cela est pertinent.

Répondre

1

Vous avez un problème avec une pile déséquilibrée: vous laissez une valeur sur la pile après la fin de la fonction. Essayez:

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

Cela l'a corrigé. Je savais qu'il y avait quelque chose de fondamental qui me manquait. Ma question semble maintenant de faible valeur, devrais-je la supprimer? – trampster

+1

@trampster Je pense que votre question est très intéressante. Le fait que la pile doit être vide/doit avoir une seule valeur (si vous avez besoin de la retourner) à la fin d'une fonction générée est quelque chose de très important qui n'est pas écrit assez fortement dans les tutoriels sur la génération de code. – xanatos