使用ILGenerator生成复制和转换对象属性的方法会产生InvalidProgramException

时间:2018-03-12 07:54:36

标签: c# .net-core cil .net-standard ilgenerator

我试图生成一个将属性从一个通用对象复制到另一个通用对象的方法,如果属性类型不同,我希望它使用TypeConverter来转换属性值。
最后我想扩大规模,以便我可以将IDataRecords直接自动映射到对象。但是现在我遇到了在属性类型不同时将属性从一个对象复制到另一个对象的问题。如果所有属性类型相同,则生成的方法可以正常工作。

我不明白为什么它无法生成,因为我在C#中编写了一个示例方法,然后查看了IL,而IL I生成应该是相同的。

以下是简化版,完整代码可在https://gist.github.com/DerekZiemba/468b84d7b5a5a289470859e261f17217

找到
public static void CopyExample(StreetAddress target, DBLocation src) {
    target.Locality = src.Locality;
    target.Latitude = (float)TypeConversion.Float32Converter.ConvertFrom(src.Latitude);
}

这是由CopyExample生成的IL:

.method public hidebysig static void 
  CopyExample(
     class ExampleILGeneratorShallowCopy.StreetAddress target, 
     class ExampleILGeneratorShallowCopy.DBLocation src
  ) cil managed 
{
  .maxstack 8

  // [36 4 - 36 35]
  IL_0000: ldarg.0      // target
  IL_0001: ldarg.1      // src
  IL_0002: callvirt     instance string ExampleILGeneratorShallowCopy.DBLocation::get_Locality()
  IL_0007: callvirt     instance void ExampleILGeneratorShallowCopy.StreetAddress::set_Locality(string)

  // [37 4 - 37 87]
  IL_000c: ldarg.0      // target
  IL_000d: ldsfld       class [System.ComponentModel.TypeConverter]System.ComponentModel.SingleConverter ExampleILGeneratorShallowCopy.TypeConversion::Float32Converter
  IL_0012: ldarg.1      // src
  IL_0013: callvirt     instance float64 ExampleILGeneratorShallowCopy.DBLocation::get_Latitude()
  IL_0018: box          [System.Runtime]System.Double
  IL_001d: callvirt     instance object [System.ComponentModel.TypeConverter]System.ComponentModel.TypeConverter::ConvertFrom(object)
  IL_0022: unbox.any    [System.Runtime]System.Single
  IL_0027: callvirt     instance void ExampleILGeneratorShallowCopy.StreetAddress::set_Latitude(float32)

  // [38 3 - 38 4]
  IL_002c: ret          

} // end of method ExampleILGeneratorShallowCopy::CopyExample

这里的ILGenerator方法应该产生与上面例子相同的输出:

public delegate void CopyIntoDelegate<T, S>(T location, S src);

public static CopyIntoDelegate<StreetAddress, DBLocation> GenerateExactExample() {
    Type targetType = typeof(StreetAddress);
    Type srcType = typeof(DBLocation);

    PropertyInfo targetLocality = targetType.GetProperty("Locality");
    PropertyInfo srcLocality = srcType.GetProperty("Locality");
    PropertyInfo targetLatitude = targetType.GetProperty("Latitude");
    PropertyInfo srcLatitude = srcType.GetProperty("Latitude");

    DynamicMethod dynmethod = new DynamicMethod("ExactExample", typeof(void), new Type[2]{ targetType, srcType }, true);
    ILGenerator gen = dynmethod.GetILGenerator();

    gen.Emit(OpCodes.Ldarg_0);
    gen.Emit(OpCodes.Ldarg_1);
    gen.Emit(OpCodes.Callvirt, srcLocality.GetMethod);
    gen.Emit(OpCodes.Callvirt, targetLocality.SetMethod);

    TypeConverter converter = TypeConversion.Float32Converter;
    FieldInfo converterField = typeof(TypeConversion).GetField(nameof(TypeConversion.Float32Converter));
    MethodInfo convertFrom = converter.GetType().GetMethod(nameof(TypeConverter.ConvertFrom), new [] { typeof(object) });

    gen.Emit(OpCodes.Ldarg_0);
    gen.Emit(OpCodes.Ldsfld, converterField);           
    gen.Emit(OpCodes.Ldarg_1);
    gen.Emit(OpCodes.Callvirt, srcLatitude.GetMethod);
    gen.Emit(OpCodes.Box, srcLatitude.PropertyType);
    gen.Emit(OpCodes.Callvirt, convertFrom);
    gen.Emit(OpCodes.Unbox_Any);
    gen.Emit(OpCodes.Callvirt, targetLatitude.SetMethod);

    gen.Emit(OpCodes.Ret);

    Delegate del = dynmethod.CreateDelegate(typeof(CopyIntoDelegate<StreetAddress, DBLocation>));
    return (CopyIntoDelegate<StreetAddress, DBLocation>)del;
}

0 个答案:

没有答案