Reflection.Emit 抛出 BadImageFormatException [英] Reflection.Emit throws BadImageFormatException

查看:60
本文介绍了Reflection.Emit 抛出 BadImageFormatException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在运行时生成一个新的类/对象.

Im trying to generate a new class/object at runtime.

阅读后如何使用 PropertyBuilder 创建私有财产,我已经设法实现了一切,一切都如我所愿.

After reading How to create a private property using PropertyBuilder, i've managed to get everyting implemented and everything is like i need it.

但是一旦我尝试实例化我的新对象,我就会收到 BadImageFormatException

But as soon as im trying to instanciate my new object, im receiving a BadImageFormatException

这似乎是一个类似的问题,但尚未解决是有什么方法可以检测 System.Reflection.Emit?

This seems to be a similar issue, but unresolved Is there any way to instrument 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;}

对于如何实例化我新创建的类型 ParamRow 的任何帮助表示赞赏.

Any help is appreciated on how to instanciate my newly created Type ParamRow.

奖励问题:为什么会出现 BadImageFormatException?

Bonus-Question: Why is there an BadImageFormatException?

附加信息:

  • .Net 框架 4.6.1
  • 编译器目标是 x86
  • 之前从没做过Reflection.Emit

推荐答案

你得到的异常的.Message很重要:

The .Message of the exception you get is the important bit:

字段标记超出范围.

这告诉您它不了解您要在 ldfld/stfld 中使用哪个字段 - 这是因为您将 方法传递给它 标记(custNameGetPropMthdBldr/custNameSetPropMthdBldr)而不是 field 标记.

This tells you that it isn't understanding what field you want to use in ldfld / stfld - which is because you're passing it the method token (custNameGetPropMthdBldr / custNameSetPropMthdBldr) instead of a field token.

您需要定义和使用一个字段:

You need to define and use a field:

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 比使用 name 更有效;这工作正常:

Note also that it is more efficient to use the Type than the name when creating objects via reflection; this works fine:

var inst = Activator.CreateInstance(finalType);

这篇关于Reflection.Emit 抛出 BadImageFormatException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆