正确实现浅拷贝和深拷贝

  为对象创建副本的技术称为拷贝(也叫克隆)。我们将拷贝分为浅拷贝和深拷贝。

  浅拷贝:将对象中的所有字段复制到新的对象(副本)中。其中,值类型字段的值被复制到副本中后,在副本中的修改不会影响到源对象对应的值。而引用类型的字段被复制到副本中的是引用类型的引用,而不是引用的对象,在副本中对引用类型的字段值做修改会影响到源对象本身。

  深拷贝:同样,将对象中的所有字段复制到新的对象中。不过,无论是对象的值类型字段,还是引用类型字段,都会被重新创建并赋值,对于副本的修改,不会影响到源对象本身。

  无论是浅拷贝还是深拷贝,微软都建议用类型继承ICloneable接口的方式明确告诉调用者:该类型可以被拷贝。当然,ICloneable接口只提供了一个声明为Clone的方法,我们可以根据需求在Clone方法内实现浅拷贝或深拷贝。一个简单的浅拷贝的实现代码如下所示:

class Employee : ICloneable
{
    public string IDCode { get; set; }
    public int Age { get; set; }
    public Department Department { get; set; }

    public object Clone()
    {
        return this.MemberwiseClone();
    }
}

class Department
{
    public string Name { get; set; }
    public override string ToString()
    {
        return this.Name;
    }
}

//调用
Employee mike = new Employee
{
    IDCode = "NB123",
    Age = 18,
    Department = new Department
    {
        Name = "Dep1"
    }
};

Employee rose = mike.Clone() as Employee;
Console.WriteLine(mike.IDCode);
Console.WriteLine(mike.Age);
Console.WriteLine(mike.Department);

Console.WriteLine("改变mike的值");

mike.IDCode = "NB456";
mike.Age = 24;
mike.Department.Name = "Dep2";

Console.WriteLine(rose.IDCode);
Console.WriteLine(rose.Age);
Console.WriteLine(rose.Department);

  输出为:

    NB123
    18
    Dep1
    改变mike的值
    NB123
    18
    Dep2

  注意到Employee的IDCode属性是string类型。理论上string类型是引用类型,但是由于该引用类型的特殊性(无论是实现还是语义),Object.MemberwiseClone方法仍旧为其创建了副本。也就是说,在浅拷贝过程,我们应该将字符串看成是值类型。

  Employee的Department属性是一个引用类型,所以,如果改变了源对象mike中的值,副本rose中的值也会随之一起变动。

  Employee的深拷贝有多种实现方法,最简单的方法是手动对字段逐个进行赋值。但这种方法容易出错,也就是说,如果类型的字段发生变化或有增减,那么该拷贝方法也要发生相应的变化,所以,建议使用序列化的形式来进行深拷贝。

  由于接口ICloneable只有一个模棱两可的Clone方法,所以如果要在一个类中同时实现深拷贝和浅拷贝,只能由我们自己实现两个额外的方法,声明为DeepClone和Shallow。Employee的最终版本看起来应该像如下的形式:

[Serializable]
class Employee : ICloneable
{
    public string IDCode { get; set; }
    public int Age { get; set; }
    public Department Department { get; set; }

    public object Clone()
    {
        return this.MemberwiseClone();
    }

    public Employee DeepClone()
    {
        using (Stream objectStream = new MemoryStream())
        {
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(objectStream, this);
            objectStream.Seek(0, SeekOrigin.Begin);
            return formatter.Deserialize(objectStream) as Employee;
        }
    }

    public Employee ShallowClone()
    {
        return Clone() as Employee;
    }
}

  需要注意在Department类中加上可序列化标签;

以上内容引用自《编写高质量代码 改善C#程序的157个建议》

时间: 2024-12-14 16:10:58

正确实现浅拷贝和深拷贝的相关文章

编写高质量代码改善C#程序的157个建议——建议14: 正确实现浅拷贝和深拷贝

建议14: 正确实现浅拷贝和深拷贝 为对象创建副本的技术称为拷贝(也叫克隆).我们将拷贝分为浅拷贝和深拷贝. 浅拷贝 将对象中的所有字段复制到新的对象(副本)中.其中,值类型字段的值被复制到副本中后,在副本中的修改不会影响到源对象对应的值.而引用类型的字段被复制到副本中的是引用类型的引用,而不是引用的对象,在副本中对引用类型的字段值做修改会影响到源对象本身. 深拷贝 同样,将对象中的所有字段复制到新的对象中.不过,无论是对象的值类型字段,还是引用类型字段,都会被重新创建并赋值,对于副本的修改,不

c++中 拷贝构造函数的深拷贝和浅拷贝--“浅拷贝”与“深拷贝”

C++中对象的复制就如同"克隆",用一个已有的对象快速地复制出多个完全相同的对象.一般而言,以下三种情况都会使用到对象的复制: (1)建立一个新对象,并用另一个同类的已有对象对新对象进行初始化,例如: class Rect { private: int width; int height; }; Rect rect1; Rect rect2(rect1); // 使用rect1初始化rect2,此时会进行对象的复制 (2)当函数的参数为类的对象时,这时调用此函数时使用的是值传递,也会产

js对象浅拷贝和深拷贝详解

js对象浅拷贝和深拷贝详解 作者:i10630226 字体:[增加 减小] 类型:转载 时间:2016-09-05我要评论 这篇文章主要为大家详细介绍了JavaScript对象的浅拷贝和深拷贝代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 本文为大家分享了JavaScript对象的浅拷贝和深拷贝代码,供大家参考,具体内容如下 1.浅拷贝 拷贝就是把父对像的属性,全部拷贝给子对象. 下面这个函数,就是在做拷贝: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var

渐析java的浅拷贝和深拷贝

作者: chenssy 出处: http://www.cnblogs.com/chenssy/ 首先来看看浅拷贝和深拷贝的定义: 浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值,这个方式被称为浅拷贝. 深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形式参数实例值.这个方式称为深拷贝 也就是说浅拷贝只复制一个对象,传递引用,不能复制实例.而深拷贝对对象内部的引用均复制,它是创建一个新的实例,并且复制实例. 对于浅拷贝

深入浅拷贝和深拷贝。初学者的笔记

浅拷贝和深拷贝都是相对拷贝构造函数来讲的 下面代码实现浅拷贝, 1 #include <stdio.h> 2 #include <iostream> 3 #include <string> 4 using namespace std; 5 6 class A 7 { 8 public : 9 A(int a=0):m_num(a){}; 10 A(const A &arr); 11 int set_num(); 12 private: 13 int m_num;

C++拷贝构造函数(浅拷贝、深拷贝)

下面举一个简单的例子说明对象之间的拷贝(此例中没有自定义拷贝构造函数,在调用拷贝构造函数的时候,编译器会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝)浅拷贝: #include<iostream> using namespace std; class CExample { private:int a; public: CExample(int b) { a=b; } void Show() { cout<<a<<endl; } }; int main(

浅拷贝,深拷贝---ios

#import <Foundation/Foundation.h> @interface Father : NSObject <NSCopying,NSMutableCopying> @property (nonatomic,copy) NSString *name; @property (nonatomic,retain) NSNumber *age; -(id) initWithName:(NSString *)name withAge:(NSNumber *) age; @e

C# 浅拷贝与深拷贝区别

浅拷贝:给对象拷贝一份新的对象.浅拷贝的定义—— 只对值类型(或string)类型分配新的内存地址.深拷贝:给对象拷贝一份全新的对象.深拷贝的定义—— 对值类型分配新的内存地址,引用类型.以及引用类型的内 http://blog.csdn.net/lai123wei/article/details/7217365 C# 浅拷贝与深拷贝区别,布布扣,bubuko.com

Python之美[从菜鸟到高手]--浅拷贝、深拷贝完全解读(copy源码分析)

可悲的我一直以为copy模块是用C写的,有时候需要深入了解deepcopy,文档描述的实在太简单,还是不知所云. 比如说最近看sqlmap源码中AttribDict的_deepcopy__有些疑惑, def __deepcopy__(self, memo): retVal = self.__class__() memo[id(self)] = retVal for attr in dir(self): if not attr.startswith('_'): value = getattr(se