Reputation: 2663
I'm trying to generate a method that will copy properties from one generic object to another and if the property type is different, I want it to use a TypeConverter to convert the property value.
Eventually I want to scale this up so I can auto map IDataRecords directly to objects. But right now I'm having an issue with copying properties from one object to the other when the property type is different. The generated method works fine if all the property types are the same.
I don't understand why it can't be generated because I wrote an example method in C# then looked at the IL, and the IL I'm generating should be the same.
Below is a simplified version, full code can be found at 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);
}
Here's the IL generated by the 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
And here's the ILGenerator method that should produce the same output as the example above:
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;
}
Upvotes: 0
Views: 369