0

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

0 回答 0