C# 深拷贝和浅拷贝

在编码中。经常会遇到赋值操作。值类型就不说了。如果是引用类型赋值。其实是引用传递,即赋值的是一个引用。比如:

            Person p1 = new Person("张三", "北京");

            Person p2 = p1;

            p2.name = "李四"; //此时,p1.name和p2.name 都被改变为 李四

当把p1赋值给p2时,那么p1和p2指向同一个地址。所有当改变p2对象的name的时候,会直接影响到p1的name

 

这就是值类型和引用类型的区别

那么这个跟深拷贝和浅拷贝有什么区别呢?

 

首先看看深拷贝(Deep Copy)和浅拷贝(Shallow Copy)的区别。(知识来源于网络)

 

 
基本类型属性引用类型属性引用类型备注
浅拷贝拷贝值拷贝引用,指向原引用的地址如果修改引用的属性,都会影响另外一个对像
深拷贝拷贝值拷贝引用和引用的内容,并创建新的实例,指向新的地址可以理解,创建一个新的对象,把原对象的内容复制到新对象中

 

 深拷贝:

  指的是拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝。这样进行深拷贝后的拷贝对象就和源对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。举个例子,一个人叫张三,然后使用克隆技术以张三来克隆另外一个人叫李四,这样张三和李四就是相互独立的,不管张三缺胳膊还是李四少腿了都不会影响另外一个人

 

浅拷贝:

  指的是拷贝一个对象时,仅仅拷贝对象的引用进行拷贝,但是拷贝对象和源对象还是引用同一份实体。此时,其中一个对象的改变都会影响到另一个对象。例如,一个人一开始叫张三,后来改名字为张老三了,可是他们还是同一个人,不管张三缺胳膊还是张老三少腿,都反应在同一个人身上。在.NET中引用类型就是一个例子。

 

我们来改造下Person类,加个Clone 方法

 public class Person
    {
        public string name { get; set; }
        public string address { get; set; }

        public Person(string name, string address)
        {
            this.name = name;
            this.address = address;
        }

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

 

F12看看MemberwiseClone的解释

 

测试代码,ok,通过浅拷贝的方式。发现。改变p2.name。p1是不受影响的,有人会问。上面的那个表格

明明说了:如果修改引用的属性,都会影响另外一个对像, 稍后再看

 

 

既然有浅拷贝,那么就有深拷贝,不过C#并没有像提供MemberwiseClone()方法一样提供一个深拷贝的方法。

深拷贝。一般是通过反序列化或者反射。或者直接new一个新的对象,new一个新的对象。简单 Person p3 = new Person();

看看反序列化的深拷贝,在person类添加深拷贝方法

    /// <summary>
        /// 深拷贝
        /// </summary>
        /// <returns></returns>
        public object DeepCopy()
        {
            BinaryFormatter bFormatter = new BinaryFormatter();
            MemoryStream stream = new MemoryStream();
            bFormatter.Serialize(stream, this);
            stream.Seek(0, SeekOrigin.Begin);
            return (Person)bFormatter.Deserialize(stream);
           
        }

然后修改测试代码, Person p2 = (Person)p1.DeepCopy(); 运行。发现有错误,标记类为可序列化即可  [Serializable]关键字

 

 

 

好了。测试看看效果

 

 

 

通过上面的 浅拷贝,和深拷贝结果对比。是不是发现结果一样。改变p2的值不会影响到p1的值。只是颜色变了而已(其实这里颜色变化是有玄机的)

刚开始。我也有点迷惑。其实深拷贝和浅拷贝之间的区别在于是否复制了子对象。

 

上面的Person类的两个属性:name和address都是string。大家知道。string在C#中是特殊的引用类型。它的值是只读的

所以在上面的测试中用了浅拷贝和深拷贝,然后改变p2的值也不会影响p1的值。不知道我这样说是否说明白了

所以我为了了测试。在person中加个引用类型的变量。也就是类类型 Room

 

那么既然是子对象(这里说的子对象,其实也就是说类的属性是一个对象)。就在person中加个子对象。创建一个新的Room类

   public class Room
    {
        public int roomId { get; set; }
        public string roomName { get; set; }

        public Room(int rId,string rName)
        {
            this.roomId = rId;
            this.roomName = rName;
        }
    }

 

在person中新增字段和构造函数

  public Room room { get; set; }

        public Person(string name, string address, Room room)
        {
            this.name = name;
            this.address = address;
            this.room = room;
        }

 

先用Clone() 方法。Clone方法是浅拷贝

 

 

 

上面是浅拷贝  当拷贝一个对象时,仅仅拷贝对象的引用进行拷贝,但是拷贝对象和源对象还是引用同一份实体。

此时,其中一个对象的改变都会影响到另一个对象,因为p1和p2的Room都指向同一个实体,当改变p1的roomId时候

直接影响到了p2的roomId。

 

下面用一个图来说明浅拷贝原理。(注:图片来源于网络)

 

 

 

然后用 DeepCopy() 方法  DeepCopy 是 深拷贝

 

上面是浅拷贝 当拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝。

这样进行深拷贝后的拷贝对象就和源对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响

所以。就算你怎么改变p1中roomId的值,p2 的roomId也不会受影响

 

下面用一个图来说明深拷贝原理。(注:图片来源于网络)

 

 

浅拷贝与赋值操作

  大多数面向对象语言中的赋值操作都是传递引用,即改变对象的指针地址,而并没有复制内存,

也没有做任何复制操作。由此可知,浅拷贝与赋值操作的区别是顶级对象的复制与否。

当然,也有一些例外情况,比如类型定义中重载赋值操作符(assignment operator),

或者某些类型约定按值传递,就像C#中的结构体和枚举类型。

 网络供图

 

 

 

拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝。这样进行深拷贝后的拷贝对象就和源对象互相独立,

其中任何一个对象的改动都不会对另外一个对象造成影响

仔细对比下。两个截图的结果。不难发现。

深拷贝时两个对象是完全“分离”的,改变其中一个,不会影响到另一个对象;浅拷贝时两个对象并未完全“分离”,改变顶级对象的内容,

不会对另一个对象产生 影响,但改变子对象的内容,则两个对象同时被改变。这种差异的产生,即是取决于拷贝子对象时复制内存还是复制指针。

深拷贝为子对象重新分配了一段内存空 间,并复制其中的内容;浅拷贝仅仅将指针指向原来的子对象。

 

参考资料:

http://www.cnblogs.com/qingteng1983/archive/2010/10/10/1847511.html

http://frankxulei.blog.51cto.com/1596834/318538/

http://www.cnblogs.com/liuconglin/p/6411259.html

http://www.cnblogs.com/ysyn/archive/2013/12/25/3490124.html

www.cnblogs.com/tinya/p/4511120.html

http://www.cnblogs.com/zhili/p/DeepCopy.html

 

dapper

 

转载于:https://www.cnblogs.com/nsky/p/4514448.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值