C#中的浅复制(shallow copy),深复制(deep copy) 及 ICloneable

时间:2022-02-22 17:53:04

  当复制一个单纯的值类型源的时候,问题就比较简单,直接复制源内容,在栈上重新生成一份拷贝即可。无所谓浅复制,深复制。

  但是当复制源是一个引用类型的对象,比如 string 对象,那么就有两种复制法了,一种是直接生成另一个引用,然后指向同一块堆内存。另一种是开辟一块新的堆内存,拷贝源的内容,然后将生成的目标引用指向新的堆内存。前者往往称为浅复制,后者称为深复制。

  当一个类中,既包含值类型变量,又包含引用类型变量时,以对引用类型变量的如前所示的处理方式的不同,也可以区别为浅复制跟深复制。就是,值类型成员变量统一深复制,根据引用类型成员变量处理方式来决定该类的复制方式是浅复制还是深复制。

 

  因为C#中所有的类型都派生自 System.Object 类,而System.Object 类有一个 protect 的方法:  object  MemberwiseClone() ,这个方法实现了对象的复制。并且是一种浅复制。因此所有的类型都自动有了一个浅复制的方法。使用方式如下:

  public  new  object  MemberwiseClone()

  {

    return base.MemberwiseClone();

  }

  而要自定义复制方式,可以实现一个 ICloneable 接口。这个接口只包含一个方法 : Clone() 。具体实现,按自己的需要设计即可。甚至可以有些引用成员变量浅复制,有些引用成员变量深复制。。。。。。当然要自定义复制方式,并非一定要实现这个接口,可以用其它喜欢的名字构成的方法,这个接口只是带来了一般接口的通用便利性而已。并没有任何关于复制的特殊特性。

 

“Effective C# Item27:避免ICloneable接口

    一般情况下,我们不建议针对类型实现ICloneable接口。因为如果一个类型支持ICloneable接口,那么该类型的所有派生类都必须实现它,而且类型中所有成员类型也都要实现ICloneable接口,或者有其他创建复制对象的机制,当我们设计的类型包含交织成网状的对象时,支持深复制会变得比较复杂。

    任何只包含内建类型成员的值类型都不需要实现ICloneable接口,一个简单的赋值语句要比Clone()方法效率更高,因为Clone()方法必须对返回值进行装箱,才能转换成一个System.Object引用。

    而对于引用类型来说,如果引用类型需要通过实现ICloneable接口的方式来表明自身支持浅复制或者深复制,这时,该类型的派生类也必须实现ICloneable接口。

    总之,对于值类型来讲,我们永远都不需要实现ICloneable接口,使用默认的赋值操作就可以了,我们应该为那些确实需要复制操作的”叶子类“实现ICloneable接口,对于那些子类可能需要实现ICloneable接口的基类来说,我们应该为其创建一个受保护的复制构造函数。除此之外,我们应该避免实现ICloneable接口。”