Использование ILGenerator для генерации метода для копирования и преобразования свойств объекта дает InvalidProgramException

Я пытаюсь сгенерировать метод, который будет копировать свойства из одного универсального объекта в другой, и если тип свойства отличается, я хочу, чтобы он использовал TypeConverter для преобразования значения свойства.
В конце концов я хочу увеличить это, чтобы я мог автоматически сопоставить IDataRecords непосредственно с объектами. Но сейчас у меня проблема с копированием свойств из одного объекта в другой, когда тип свойства отличается. Сгенерированный метод работает нормально, если все типы свойств одинаковы.

Я не понимаю, почему он не может быть сгенерирован, потому что я написал пример метода на C#, а затем посмотрел на IL, и генерируемый мной IL должен быть таким же.

Ниже приведена упрощенная версия, полный код можно найти по адресу 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);
}

Вот IL, сгенерированный CopyExample:

.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 ответов

Другие вопросы по тегам