Reflection.Emit выбрасывает исключение BadImageFormatException

Я пытаюсь создать новый класс / объект во время выполнения.

Прочитав Как создать частную собственность с помощью PropertyBuilder, я Мне удалось все реализовать, и все как будто мне нужно.

Но как только я пытаюсь создать экземпляр своего нового объекта, я получаю BadImageFormatException

Кажется, это аналогичная проблема, но нерешенная Есть ли способ для инструмента System.Reflection.Emit?

Вот мой код:

Поле:

internal class Field {
      public string FieldName;
      public Type FieldType;
      public string Value;
    }

Код генератора:

var xx = new List<Field>(new[] { new Field { FieldName = "Name", FieldType = typeof(string), Value = "Hello World" },
        new Field { FieldName = "Id", FieldType = typeof(int), Value = "1" } });
      this.DoVodoo(xx);

Магия

private dynamic DoVodoo(IEnumerable<Field> fields) {
      var aName = new AssemblyName("DynamicAssemblyExample");
      var ab = AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave);

      var mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");

      // Create class with all needed Properties
      var tb = mb.DefineType("ParamRow", TypeAttributes.Public, typeof(object));
      foreach (var field in fields) {
        var pb = tb.DefineProperty(field.FieldName, PropertyAttributes.None, CallingConventions.HasThis, field.FieldType, null);

        var getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
        // Define the "get" accessor method for the Property.
        var custNameGetPropMthdBldr = tb.DefineMethod($"get_{field.FieldName}", getSetAttr, typeof(string), Type.EmptyTypes);

        var custNameGetIL = custNameGetPropMthdBldr.GetILGenerator();

        custNameGetIL.Emit(OpCodes.Ldarg_0);
        custNameGetIL.Emit(OpCodes.Ldfld, custNameGetPropMthdBldr);
        custNameGetIL.Emit(OpCodes.Ret);

        // Define the "set" accessor method for CustomerName.
        var custNameSetPropMthdBldr = tb.DefineMethod($"set_{field.FieldName}", getSetAttr, null, new[] { typeof(string) });

        var custNameSetIL = custNameSetPropMthdBldr.GetILGenerator();

        custNameSetIL.Emit(OpCodes.Ldarg_0);
        custNameSetIL.Emit(OpCodes.Ldarg_1);
        //custNameSetIL.Emit(OpCodes.Stfld, custNameGetPropMthdBldr);
        custNameSetIL.Emit(OpCodes.Stfld, custNameSetPropMthdBldr);
        custNameSetIL.Emit(OpCodes.Ret);

        // Last, we must map the two methods created above to our PropertyBuilder to 
        // their corresponding behaviors, "get" and "set" respectively. 
        pb.SetGetMethod(custNameGetPropMthdBldr);
        pb.SetSetMethod(custNameSetPropMthdBldr);
      }

      var finalType = tb.CreateType();

      var result = new List<object>();

      foreach (var field in fields) {
        var inst = ab.CreateInstance(finalType.Name);
        finalType.GetProperty(field.FieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).SetValue(inst, field.Value); //<-- Here comes the trouble
        result.Add(inst);
      }
      return result;}

Приветствуется любая помощь в том, как создать экземпляр моего недавно созданного Type ParamRow.

Бонус-вопрос: Почему там BadImageFormatException?

Дополнительная информация:

  • .Net-Framework 4.6.1
  • Цель компилятора - x86
  • Никогда раньше не делал Reflection.Emit

person lokusking    schedule 15.07.2016    source источник
comment
Я собираюсь взглянуть на это в отладчике и посмотреть, смогу ли я обнаружить ошибку, но: если вам нужен хороший способ избежать их и получить хорошие сообщения об ошибках, попробуйте Sigil - это оболочка вокруг испускания IL (но концептуально то же самое), и она разработана, чтобы затруднить отказ (или, по крайней мере, , легко понять, почему это не удалось)   -  person Marc Gravell    schedule 15.07.2016


Ответы (1)


.Message полученного исключения является важным элементом:

Маркер поля вне допустимого диапазона.

Это говорит о том, что он не понимает, какое поле вы хотите использовать в ldfld / stfld - это потому, что вы передаете ему токен method (custNameGetPropMthdBldr / custNameSetPropMthdBldr) вместо поля . токен.

Вам необходимо определить и использовать поле:

var fb = tb.DefineField("__" + field.FieldName, field.FieldType, FieldAttributes.Private);
// ...
custNameGetIL.Emit(OpCodes.Ldarg_0);
custNameGetIL.Emit(OpCodes.Ldfld, fb);
custNameGetIL.Emit(OpCodes.Ret);
// ...
custNameSetIL.Emit(OpCodes.Ldarg_0);
custNameSetIL.Emit(OpCodes.Ldarg_1);
custNameSetIL.Emit(OpCodes.Stfld, fb);
custNameSetIL.Emit(OpCodes.Ret);

Также обратите внимание, что при создании объектов через отражение более эффективно использовать Type, чем имя; это отлично работает:

var inst = Activator.CreateInstance(finalType);
person Marc Gravell    schedule 15.07.2016
comment
Вы, сэр, палка-выручалочка. Для меня немного неловко, что я скучал по резервному полю * -.- - person lokusking; 15.07.2016
comment
@lokusking Кажется, я припоминаю, что Sigil показывал бы эту ошибку во время компиляции; при вызове метода для выдачи доступа к полю сигнатура метода требует, чтобы вы передали ему объект поля. Это отличная библиотека для облегчения работы с IL. - person Marc Gravell; 15.07.2016
comment
Обычно я бы попробовал, но из-за политики компании очень сложно получить что-то от Nuget. Я попробую дома - person lokusking; 15.07.2016
comment
@Marc Gravell, это ошибка времени компиляции или более явное исключение времени выполнения? - person Cosmin Sontu; 16.07.2018