【问题标题】:Implementing the ICloneable Interface C# (Deep Cloning)实现 ICloneable 接口 C#(深度克隆)
【发布时间】:2010-12-14 16:42:04
【问题描述】:

我正在查看为其中一个类实现 ICloneable 接口的代码。

课程如下:

public class TempClass
{
  String[] names;
  String[] values;
}

创建了一个实现TempClass的部分类

public partial class TempClass:ICloneable
{
   public Object Clone()
   {
      TempClass cloneClass = new TempClass();
      String[] cloneNames = new String[this.names.Length - 1];
      String[] cloneValues = new String[this.values.Length -1];

      Array.Copy(this.names,cloneNames,this.names.Length);
      Array.Copy(this.values,cloneValues,this.values.Length);

      cloneClass.names = cloneNames;
      cloneValues.values = cloneValues;

      return cloneClass;
   }
}

我想知道这是否是对对象进行深层复制的有效方法?这里引发标志的是中间结构cloneNamescloneValues,它们用于复制原始对象的值,并让成员变量Names 和Values 指向它,然后返回在克隆方法。

任何有关此 sn-p 的反馈将不胜感激

谢谢

【问题讨论】:

    标签: c# reference clone


    【解决方案1】:

    嗯,有一个问题 - 你没有创建正确大小的新字符串数组。应该是:

    String[] cloneNames = new String[this.names.Length];
    String[] cloneValues = new String[this.values.Length];
    

    或者作为一个更简单的解决方案:

    String[] cloneNames = (String[]) this.names.Clone();
    String[] cloneValues = (String[]) this.values.Clone();
    

    除此之外,应该没问题。您能否更详细地解释您的担忧?

    【讨论】:

    • 还有,为什么不直接设置克隆对象的成员数组呢?
    • @Jeff:因为那将是一个浅克隆; OP 显然想要一个深度克隆,以便在事后更改原始数组的内容不会更改新对象的数组。
    • D'oh,我的问题并不清楚 - 我的意思是,中间变量添加了什么?为什么不cloneClass.names = (String[] this.names.Clone();
    • 谢谢杰夫。那时候我想过那个。从内存模型的角度来看,我想知道直接设置成员数组与维护对中间结构的引用有何影响。 Tony - Array.Clone() 不会对数据结构中的值进行浅拷贝吗?
    • @sc_ray - 没错。然而,使用strings 会使情况有点混乱,因为它们是不可变的。当您“修改”数组中string 元素的值时,实际上是用不同的引用(如值类型)替换它。因此,使用TempClass.Clone 创建的对象不受原始更改的影响——但在一般情况下并非如此。例如,对于常规引用类型(例如,FileInfo 实例的数组),它就不是真的。对于它的价值,Array.Copy 也执行浅拷贝。
    【解决方案2】:

    Array.CloneArray.Copy 都创建了浅拷贝(如链接的 API 文档中所述),但使用字符串数组会使问题有些模糊,因为字符串在 .NET 中是不可变的。当你这样做时:

    stringArray[0] = "first value";
    stringArray[0] = "second value";
    

    第一个字符串实例被一个不同的实例替换,所以看起来你已经做了一个深拷贝(对于字符串数组,你也可以这样做)。

    要使用普通引用类型的数组执行对象的深拷贝,您需要创建数组元素的新实例。假设TempClass 存储了一个Cookie 对象数组而不是字符串:

    public Object Clone() {
        TempClass clone = new TempClass();
    
        clone.cookieArray = new Cookie[this.cookieArray.Length];
        for(int i = 0; i < this.cookieArray.Length; i++) {
            Cookie cookie  = this.cookieArray[i];
            clone.cookieArray[i] = new Cookie(cookie.Name, cookie.Value, cookie.Path, cookie.Domain);
        }
    }
    

    【讨论】:

    • 虽然这回答了问题,但这不是有效的“Clone()”实现。如果“this”是不重新实现“Clone()”的 TempClass 的子类,则建议的代码将不会返回“this”的有效克隆。 “Clone()”的结果值必须始终通过调用“MemberwiseClone()”获得。
    【解决方案3】:

    你可以重写你的克隆方法如下,否则小马是对的。

    public Object Clone()
    {
        return new TempClass
        {
            names = this.names == null? null:this.names.ToArray(),
            values = this.values == null? null:this.values.ToArray()
        };
    }
    

    它可能比克隆稍慢,但它可以工作。

    【讨论】:

    • 谢谢尤里。但是上面给出的代码不会返回对原始数组的引用,而不是将数组及其内容复制到克隆对象吗?
    • 不,它会创建一个新数组。
    猜你喜欢
    • 2010-09-14
    • 2017-09-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    • 2013-02-07
    • 2016-06-27
    • 1970-01-01
    相关资源
    最近更新 更多