如何在C#.NET中深层复制不同类型的对象

我需要按字段名称映射ObjectV1和ObjectV2之间的所有字段值和子集合。 ObjectV2与ObjectV1位于不同的namspace中。

模板ClassV1和ClassV2之间的inheritance已被打折扣,因为这两个类需要独立发展。 我考虑过使用reflection(慢速)和二进制序列化(也很慢)来执行公共属性的映射。

有首选方法吗? 还有其他选择吗?

作为每次使用reflection的替代方法,您可以创建一个使用Reflection.Emit动态创建复制方法的辅助类 – 这意味着您只能在启动时获得性能。 这可以为您提供所需的灵活性和性能组合。

由于Reflection.Emit非常笨重,我建议检查一下这个 Reflector插件,这对构建这种代码非常有用。

这是什么版本的.NET?

对于浅拷贝:

在3.5中,您可以预编译Expression来执行此操作。 在2.0中,您可以非常轻松地使用HyperDescriptor来执行相同操作。 两者都将远远超过反思。

MiscUtil有一个预先实现的Expression方法 – PropertyCopy

 DestType clone = PropertyCopy.CopyFrom(original); 

(浅浅)

BinaryFormatter(在问题中)不是一个选项 – 由于原始类型和目标类型不同,它根本不起作用。 如果数据是基于契约的,那么如果所有合同名称都匹配, XmlSerializer或DataContractSerializer将起作用,但如果可能的话,上面的两个(浅)选项会更快。

另外 – 如果你的类型标有常见的序列化属性( XmlTypeDataContract ),那么protobuf-net 可以 (在某些情况下)为你做深度复制/更改类型:

 DestType clone = Serializer.ChangeType(original); 

但这取决于具有非常相似模式的类型(实际上,它不使用名称,它在属性上使用显式“顺序”等)

您可能需要查看AutoMapper ,这是一个专门在对象之间复制值的库。 它使用约定优于配置,因此如果属性确实具有exaxt相同的名称,它将为您完成几乎所有的工作。

这是我建立的解决方案:

  ///  /// Copies the data of one object to another. The target object gets properties of the first. /// Any matching properties (by name) are written to the target. ///  /// The source object to copy from /// The target object to copy to public static void CopyObjectData(object source, object target) { CopyObjectData(source, target, String.Empty, BindingFlags.Public | BindingFlags.Instance); } ///  /// Copies the data of one object to another. The target object gets properties of the first. /// Any matching properties (by name) are written to the target. ///  /// The source object to copy from /// The target object to copy to /// A comma delimited list of properties that should not be copied /// Reflection binding access public static void CopyObjectData(object source, object target, string excludedProperties, BindingFlags memberAccess) { string[] excluded = null; if (!string.IsNullOrEmpty(excludedProperties)) { excluded = excludedProperties.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); } MemberInfo[] miT = target.GetType().GetMembers(memberAccess); foreach (MemberInfo Field in miT) { string name = Field.Name; // Skip over excluded properties if (string.IsNullOrEmpty(excludedProperties) == false && excluded.Contains(name)) { continue; } if (Field.MemberType == MemberTypes.Field) { FieldInfo sourcefield = source.GetType().GetField(name); if (sourcefield == null) { continue; } object SourceValue = sourcefield.GetValue(source); ((FieldInfo)Field).SetValue(target, SourceValue); } else if (Field.MemberType == MemberTypes.Property) { PropertyInfo piTarget = Field as PropertyInfo; PropertyInfo sourceField = source.GetType().GetProperty(name, memberAccess); if (sourceField == null) { continue; } if (piTarget.CanWrite && sourceField.CanRead) { object targetValue = piTarget.GetValue(target, null); object sourceValue = sourceField.GetValue(source, null); if (sourceValue == null) { continue; } if (sourceField.PropertyType.IsArray && piTarget.PropertyType.IsArray && sourceValue != null ) { CopyArray(source, target, memberAccess, piTarget, sourceField, sourceValue); } else { CopySingleData(source, target, memberAccess, piTarget, sourceField, targetValue, sourceValue); } } } } } private static void CopySingleData(object source, object target, BindingFlags memberAccess, PropertyInfo piTarget, PropertyInfo sourceField, object targetValue, object sourceValue) { //instantiate target if needed if (targetValue == null && piTarget.PropertyType.IsValueType == false && piTarget.PropertyType != typeof(string)) { if (piTarget.PropertyType.IsArray) { targetValue = Activator.CreateInstance(piTarget.PropertyType.GetElementType()); } else { targetValue = Activator.CreateInstance(piTarget.PropertyType); } } if (piTarget.PropertyType.IsValueType == false && piTarget.PropertyType != typeof(string)) { CopyObjectData(sourceValue, targetValue, "", memberAccess); piTarget.SetValue(target, targetValue, null); } else { if (piTarget.PropertyType.FullName == sourceField.PropertyType.FullName) { object tempSourceValue = sourceField.GetValue(source, null); piTarget.SetValue(target, tempSourceValue, null); } else { CopyObjectData(piTarget, target, "", memberAccess); } } } private static void CopyArray(object source, object target, BindingFlags memberAccess, PropertyInfo piTarget, PropertyInfo sourceField, object sourceValue) { int sourceLength = (int)sourceValue.GetType().InvokeMember("Length", BindingFlags.GetProperty, null, sourceValue, null); Array targetArray = Array.CreateInstance(piTarget.PropertyType.GetElementType(), sourceLength); Array array = (Array)sourceField.GetValue(source, null); for (int i = 0; i < array.Length; i++) { object o = array.GetValue(i); object tempTarget = Activator.CreateInstance(piTarget.PropertyType.GetElementType()); CopyObjectData(o, tempTarget, "", memberAccess); targetArray.SetValue(tempTarget, i); } piTarget.SetValue(target, targetArray, null); } 

如果速度是一个问题,您可以使reflection过程脱机并生成用于映射公共属性的代码。 您可以在运行时使用轻量级代码生成或通过构建C#代码进行完全脱机来进行编译。

如果您控制目标对象的实例化,请尝试使用JavaScriptSerializer 。 它不会吐出任何类型的信息。

 new JavaScriptSerializer().Serialize(new NamespaceA.Person{Id = 1, Name = "A"}) 

回报

 {Id: 1, Name: "A"} 

从这里可以反序列化具有相同属性名称的任何类。

如果速度是一个问题,您应该在方法本身中实现克隆方法。