vendredi 24 février 2017

How do I create a Static Field using Reflection.Emit

I'm trying to create a static field using Reflection.Emit.

However I get a InvalidProgramException when I try and load it.

The following code reproduces my problem.

The line: generator.Emit(OpCodes.Ldsfld, builderField); causes a InvalidProgramException, without this line the program runs fine and returns "Test" as expected

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

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

        var builderField = typeBuilder.DefineField("_builder", typeof(int), 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();

I know I'm not using the StringBuilder for anything just putting it on the stack, and that it will be null as it is not initialized, however it a simplified version of a larger program which lazy initializes the field, so I need to load it onto the stack so I can check it for null. However the ldsfld instruction crashes the program before I can do any of that.

ILSPY for a working version of this code looks like this:

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

What is the correct way to create and use a static field using Reflection.Emit?

I'm running on .net core 1.1 on linux if that is relevant.





Aucun commentaire:

Enregistrer un commentaire