我正在尝试生成一种将属性从一个通用对象复制到另一个的方法,如果属性类型不同,我希望它使用 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);
}
下面是 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;
}