深拷贝与浅拷贝


 1. 概念

  1)深拷贝

 深拷贝:指的是拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝。这样进行深拷贝后的拷贝对象就和源对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。举个例子,一个人叫张三,然后使用克隆技术以张三来克隆另外一个人叫李四,这样张三和李四就是相互独立的,不管张三缺胳膊还是李四少腿了都不会影响另外一个人。在.NET领域,值类型对象就是典型的例子,值类型赋值内部执行深拷贝,如int, Double以及结构体和枚举等。具体例子如下所示:

int source = 123;

// 值类型赋值内部执行深拷贝

int copy = source;

// 对拷贝对象进行赋值不会改变源对象的值

copy = 234;

// 同样对源对象赋值也不会改变拷贝对象的值

source = 345;

 

 2)浅拷贝

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

 

该例子内类的属性只有引用类型,因此等号赋值浅拷贝的效果相同,即对象的改变影响到源对象。

public class Person

    {

       public string Name { get; set; }

    }

 

   class Program

    {

       static void Main(string[] args)

       {

           Person sourceP = new Person() { Name = "张三"};

           Person copyP = sourceP; // 浅拷贝(等号赋值)

           copyP.Name = "张老三"; // 拷贝对象改变Name值

           // 结果都是"张老三",因为实现的是浅拷贝,一个对象的改变都会影响到另一个对象

           Console.WriteLine("Person.Name: [SourceP: {0}] [CopyP:{1}]",sourceP.Name, copyP.Name);

           Console.Read();

       }

}

3)应用场景

当一个类内既有值类型属性,又有引用类型属性时;

 

A.等号赋值实现:

等号赋值相当于把引用地址赋给新对象,此时两个对象共用同一份地址指向的值,任意一个对象改变指向的值,源数据都会受到影响(即值类型的值和引用类型的值都会被改变)。

 

等号结果,改变对象属性值都会反映到源数据上;

 

 

 

B.浅拷贝实现:

通过实现ICloneable 接口:支持克隆,即用与现有实例相同的值创建类的新实例

 

使用MemberwiseClone 方法:创建当前 System.Object 的浅表副本。

 

class p : ICloneable

    {

       public object Clone()

       {

           return this.MemberwiseClone();

       }

}

浅拷贝的效果是:对于内部的引用类型的属性(Class 对象和数组),则Copy 一份地址。[ 改变B 时,A也被改变了 ]

而对于其它内置值类型的属性(int / string / enum / struct /object 类型),则Copy 一份值。[ 改变B 时,A不会被改变]

 

 

总结:因此可以看出,浅拷贝的实现会对值类型的属性进行深拷贝(两个对象的属性值相互独立),而引用类型(除string类型外)执行浅拷贝,对于一个对象中全是除String类型外的引用类型时,此时等号和浅拷贝是一样的效果,虽然结果一样,但是本质不一样,浅拷贝是有对属性拷贝的过程的,而等号完全没有对属性的拷贝过程

 

注:string效果类似值类型,表现上被.NET优化为值类型,可查看专题【string不变特性】内容。

 

4)案例探究

问:浅拷贝对引用类型来说就是拷贝它的引用地址,那么直接用一个等号就完成了,为什么还需要那么麻烦的去实现接口呢?

 

答:其实这个问题自己试下就知道了,用等号引用的是同一个对象,引用对象的改变都会反映到源对象中,而浅拷贝的实现会对值类型的属性进行深拷贝,而引用类型(除string类型外)执行浅拷贝,对于一个对象中全是除String类型外的引用类型时,此时等号和浅拷贝是一样的效果,虽然结果一样,但是本质不一样,浅拷贝是有对属性拷贝的过程的,而等号完全没有对属性的拷贝过程

 

 

 

 

 

 

classProgram

   {

       class浅拷贝与等号赋值

       {

            staticvoid Main(string[] args)

            {

                DemoClass A = newDemoClass();

                //创建实例A的副本 --> 新对象实例B

 

                DemoClass B = (DemoClass)A.Clone();【浅拷贝】

                DemoClass B = A;【等号赋值】

                

//更改对象各个属性值

                B._int = 2;

                Console.WriteLine(" int\t\t  A:{0}  B:{1}",A._int, B._int);

 

                B._string = "2";

                Console.WriteLine(" string\t  A:{0} B:{1}", A._string, B._string);

 

                B._enum = myEnum._2;

                Console.WriteLine(" enum\t\t  A:{0}  B:{1}", (int)A._enum, (int)B._enum);

 

                B._struct._int = 2;

                Console.WriteLine(" struct\t  A:{0} B:{1}",

                                 A._struct._int, B._struct._int);

 

                B._class._string = "2";

                Console.WriteLine(" class\t\t  A:{0}  B:{1}",

                                 A._class._string, B._class._string);

 

                B.arrInt[0] = 2;

                Console.WriteLine(" intArray\t  A:{0} B:{1}",

                                  A.arrInt[0],B.arrInt[0]);

 

                B.arrString[0] = "2";

                Console.WriteLine(" stringArray\t  A:{0} B:{1}",

                                 A.arrString[0], B.arrString[0]);

 

                Console.ReadKey();

            }

       }

 

        //枚举

       publicenummyEnum

       { _1 = 1, _2 = 2 }

 

       //结构体

       publicstructmyStruct

       {

            publicint _int;

            public myStruct(int i)

            { _int = i; }

       }

 

       //

       classmyClass

       {

            publicstring _string;

            public myClass(string s)

            { _string = s; }

       }

 

       //ICloneable:创建作为当前实例副本的新对象。

       classDemoClass : ICloneable

       {

            publicint _int = 1;

            publicstring _string = "1";

            publicmyEnum _enum = myEnum._1;

            publicmyStruct _struct = newmyStruct(1);

            publicmyClass _class = newmyClass("1");

            //数组

            publicint[] arrInt = newint[] { 1 };

            publicstring[] arrString = newstring[] { "1" };

 

            //返回此实例副本的新对象

            publicobject Clone()

            {

                //MemberwiseClone:返回当前对象的浅表副本(它是Object对象的基方法)

                returnthis.MemberwiseClone();

            }

       }

}

 

DemoClass B = A;【等号赋值】

源数据改变,值类型的属性值和引用类型属性值也都全部改变。(等号赋值,指向的源数据,一变全变,没有例外)

                

 

DemoClass B = (DemoClass)A.Clone();【浅拷贝】

浅拷贝实现,值类型属性值不变保持相互独立,引用类型(除了string)其他引用的源数据改变,属性值也全部改变(浅拷贝,值类型保持独立,引用类型一变全变,string不可变性)

          

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值