【问题标题】:Reflection.Emit throws BadImageFormatExceptionReflection.Emit 抛出 BadImageFormatException
【发布时间】:2016-07-15 06:43:33
【问题描述】:

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

在阅读How to create a private property using PropertyBuilder 之后,我已经成功实现了一切,一切都像我需要的一样。

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

这似乎是一个类似的问题,但未解决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的帮助。

奖金问题: 为什么会有BadImageFormatException

附加信息:

  • .Net-Framework 4.6.1
  • 编译器目标是 x86
  • 以前从来没有Reflection.Emit

【问题讨论】:

  • 我将在调试器中查看它,看看是否可以发现错误,但是:如果您想要一种避免它们并获得良好错误消息的好方法,请尝试@987654323 @ - 它是 IL emit 的包装器(但概念上相同),旨在使其难以失败(或者至少,很容易找出失败的原因)

标签: c# reflection reflection.emit


【解决方案1】:

你得到的异常的.Message是重要的一点:

字段标记超出范围。

这告诉您它不了解您要在 ldfld / stfld 中使用的字段 - 这是因为您正在向它传递 method 令牌 (custNameGetPropMthdBldr / custNameSetPropMthdBldr) 而不是 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 比使用名称 更有效;这工作正常:

var inst = Activator.CreateInstance(finalType);

【讨论】:

  • 先生,您是救生员。对我来说有点尴尬,我错过了背景 *-.-
  • @lok​​usking 我似乎记得 Sigil 在编译时会显示此错误;当调用该方法以发出字段访问时,方法签名要求您将一个字段对象传递给它。这是一个很棒的库,可以让 IL 更容易正确。
  • 通常我会试一试,但公司政策使得从 Nuget 获得东西真的很困难。我会在家里试试
  • @Marc Gravell,是编译时错误,还是更明确的运行时异常?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-18
  • 2016-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多