【问题标题】:How to copy ref types (deepcopy)?如何复制 ref 类型(deepcopy)?
【发布时间】:2009-05-10 17:39:01
【问题描述】:

我在一个接受 IEnumerable 的集合中有一个构造函数。我想枚举并使用这些项目创建一个新集合,但不引用相同的项目。 Items 可以是 value 和 ref 类型:

CustomCollection cc = new CustomCollection (IEnumerable<T> items)
{
    foreach (var item in items)
    {
        this.Add(item); // justs adds the reference for ref types.
    }
}

编辑:.NET 集合具有相同的方法,并且不进行深度复制。为什么您想要一个引用与原始元素相同的元素的集合?

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    如果相关类支持序列化,您可以将集合序列化为临时流,然后将它们反序列化回新集合。

    有些类支持 ICloneable,但具体做什么由程序员决定。它可能是深拷贝,也可能不是。

    当然,并不是所有的类都支持序列化和克隆,所以你想要的不可能对所有类型都做。

    您需要为此设置一些限制。

    【讨论】:

      【解决方案2】:

      如果对象是可序列化的,你可以像这样制作深拷贝:

       private object GetCopy(object original)
       {
           if (original == null)
           {
               return null;
           }
      
           object result;
           using (MemoryStream stream = new MemoryStream())
           {
               BinaryFormatter formatter = new BinaryFormatter();
               formatter.Serialize(stream, original);
               stream.Position = 0;
               result = formatter.Deserialize(stream);
               stream.Close();
           }
           return result;
       }
      

      【讨论】:

      • 是的,我能够复制和粘贴相同的内容。您需要对它们进行深度克隆,否则您只是指向相同的引用。
      • @Chris:对不起,我不明白你的评论。上面的代码来自我目前工作的项目(我们使用 if 从缓存中检索对象的副本)。代码提供了深拷贝机制;返回的对象与原始对象完全分离。
      【解决方案3】:

      我认为,如果您的项目支持 IClonable 接口(接口函数的正确实现),您将能够调用创建深层副本的 .clone 函数。

      【讨论】:

      • ICloneable 指定不当(未标记为浅或深)且很少支持。
      【解决方案4】:

      执行深拷贝的效果差异很大,这就是 CLR 不会自动提供深拷贝的原因。例如,想象一下,如果您的深拷贝复制了持有操作系统管理资源(如 SQL 连接、套接字、自定义 GDI 画笔等)句柄的对象会发生什么。

      您确实需要坚持使用上述建议的方法。如果您认为您相信来源可以正确实现接口,请检查 T 是否为 ICloneable。如果不是(信任源或 T 不可克隆),则检查对象是否标记为 ISerializable 或具有 SerializableAttribute,然后序列化/反序列化为新对象。

      如果 T 不可克隆或不可序列化,那么您可能应该抛出异常而不是尝试进行深层复制。

      【讨论】:

        【解决方案5】:

        你不能。并非所有对象都支持类似克隆的方法。

        【讨论】:

          【解决方案6】:

          我不知道语法,因为我没有经常使用它,但是这不能用反射来完成吗?我意识到反射通常会导致性能下降,但你也许可以做一些 switch-case 的事情,如下所示:

          (伪代码)

          if(item.isValueType) {
              newCol.Add(item)
          } elseif(item.isRefType && item.SupportsICloneable) {
              newCol.Add(item.clone())
          } else {
               doReflectionCopy()
          }
          

          您还可以为可序列化检查添加代码并包含 Frederik 的方法。

          【讨论】:

            猜你喜欢
            • 2018-06-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-11-09
            • 1970-01-01
            相关资源
            最近更新 更多