Использование 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;
}