如何在不访问源代码的情况下“深入”克隆第三方类的属性?

.NET框架的ICloneable接口通常提供了一种支持克隆类实例的方法。

但是如果我有多个第三方类 ,并且不想单独关注每个属性,那么如何有效地克隆这些类的对象呢? (这些类的源代码不可用)。 有没有办法使用generics和扩展方法 ?

我需要的是一个深度克隆,它创建一个包含所有属性和(子)对象的精确副本。


示例:假设您要在LinqPad中克隆UserQuery对象:

 void Main() { UserQuery uc=this; var copy=uc.CreateCopy(); // clone it } 

我正在寻找的是一个CreateCopy()扩展,它允许创建一个副本,而不必处理这个类的细节,因为我没有UerQuery的源UerQuery

(请注意, UserQuery只是一个示例,用于显示我需要的内容,它也可以是PDF文档类,用户控件类,ADO.NET类或其他任何内容)。

我目前有两个不同的解决方案 ,一个有一个没有使用reflection:


1.)使用通用扩展方法 (加上reflection ),你可以在C#中这样做:

 public static class Extension { public static T CreateCopy(this T src) where T: new() { if (src == null) return default(T); // just return null T tgt = new T(); // create new instance // then copy all properties foreach (var pS in src.GetType().GetProperties()) { foreach (var pT in tgt.GetType().GetProperties()) { if (pT.Name != pS.Name) continue; (pT.GetSetMethod()).Invoke(tgt, new object[] { pS.GetGetMethod().Invoke(src, null) }); } }; return tgt; } // method } // class 

这非常强大,因为现在您可以克隆每个对象,而不仅仅是来自您编写的类的对象,而是来自所有类, 包括 .NET Framework 的系统类 。 由于reflection, 您不需要知道它的属性,它们会自动复制。

要使用CreateCopy()方法,假设您有Customer类和Order类,并且需要创建副本(而不是引用)但使用新ID。 然后你可以做以下事情:

 Order CopyOrderWithNewPK(Order item) { Order newItem = item.CreateCopy(); // use ext. method to copy properties newItem.OrderId = new Guid(); // create new primary key for the item return newItem; } 

毫不奇怪,对于Customer类,它看起来是一样的:

 Customer CopyCustomerWithNewPK(Customer item) { Customer newItem = item.CreateCopy(); // use ext. method to copy properties newItem.CustomerId = new Guid(); // create new primary key for the item return newItem; } 

请注意 ,示例类中定义的所有属性的值都会自动复制。 如果您不拥有源代码,甚至可以克隆第三方程序集的对象。 权衡的是,反思方法较慢。


2.)在这个问题的启发 ,还有另一种没有反思的方法。 一个优点是它甚至能够克隆entity framework的对象(例如,将实体对象附加和重新附加到不同的数据上下文):

 // using System.Runtime.Serialization; public class Cloner { readonly DataContractSerializer _serializer = new DataContractSerializer(typeof(T)); ///  /// Clone an object graph ///  ///  ///  public T Clone(T graph) { MemoryStream stream = new MemoryStream(); _serializer.WriteObject(stream, graph); stream.Seek(0, SeekOrigin.Begin); return (T)_serializer.ReadObject(stream); } } 

为了不破坏上面的示例,您可以更改扩展方法CreateCopy ,如下所示:

 public static class Extension { public static T CreateCopy(this T src) where T: new() { return (new Cloner()).Clone(src); } } 

注意:虽然Cloner使用的是System.Runtime.Serialization ,但是克隆的对象不需要可序列化。 这可能是一个优点,我见过的其他解决方案只能克隆可序列化的对象。

如果您实施ICloneable您不必关心每个属性! 你的答案是好的,但对我来说,它的代码和reflection都很低,性能很差(!),我更喜欢使用

 class Test : ICloneable { public int A { get; set; } public int B { get; set; } #region ICloneable-Member public object Clone() { return base.MemberwiseClone(); } #endregion }