【问题标题】:How can I "deeply" clone the properties of closed source 3rd party classes?如何“深入”克隆封闭源代码 3rd 方类的属性?
【发布时间】:2012-10-06 07:48:48
【问题描述】:

.NET 框架的ICloneable 接口通常提供一种方法来支持类实例的cloning

但是,如果我有多个 3rd 方类,并且不想单独关心每个属性,那么如何有效地克隆这些类的对象? (这些类的源代码不可用)。 有没有办法使用genericsextension methods

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


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

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

我正在寻找的是一个CreateCopy() 扩展,它允许创建一个副本而无需处理此类的细节,因为我不拥有UerQuery 的源代码。

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

【问题讨论】:

    标签: c# .net class object clone


    【解决方案1】:

    我目前有 2 种不同的解决方案,一种使用反射,一种不使用反射:


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

    public static class Extension
    {
        public static T CreateCopy<T>(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 框架。并且由于反射,您不需要知道它的属性,它们会被自动复制。

    示例

    要使用方法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;
    }
    

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


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

    // using System.Runtime.Serialization;
    public class Cloner<T>
    {
        readonly DataContractSerializer _serializer 
                = new DataContractSerializer(typeof(T));
    
        /// <summary>
        /// Clone an object graph
        /// </summary>
        /// <param name="graph"></param>
        /// <returns></returns>
        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<T>(this T src)
            where T: new()
        {
                return (new Cloner<T>()).Clone(src);
        }   
    }   
    

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

    【讨论】:

      【解决方案2】:

      如果您实现ICloneable,您就不必关心每个属性!你的回答很好,但对我来说,它的代码和反射性能都很差(!),我更喜欢使用

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

      【讨论】:

      • +1 表示直接的方法。你是对的,反射更慢。就我而言,我必须克隆一个我不拥有源代码的第 3 方程序集的对象。因此,唯一的方法是使用扩展方法和泛型。
      猜你喜欢
      • 1970-01-01
      • 2019-10-22
      • 1970-01-01
      • 2017-11-17
      • 2011-09-16
      • 1970-01-01
      • 2012-03-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多