博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浅谈C#浅拷贝和深拷贝
阅读量:6258 次
发布时间:2019-06-22

本文共 4020 字,大约阅读时间需要 13 分钟。

近来爱上一本书《编写高质量代码,改善C#程序的157个建议》,我想很多人都想编写高质量的代码,因为我们不仅仅是码农,更是一名程序员。

从今天开始,我将每天和大家分享这本书中的内容,并加上自己的理解,希望可以帮助到更多和我一样盲目的程序员们。

今天我们谈谈C#中的对象拷贝问题;

所谓的对象拷贝,其实就是为对象创建副本,C#中将拷贝分为两种,分别为浅拷贝和深拷贝;

所谓浅拷贝就是将对象中的所有字段复制到新的副本对象中;浅拷贝对于值类型与引用类型的方式有区别,值类型字段的值被复制到副本中后,在副本中的修改不会影响源对象对应的值;然而对于引用类型的字段被复制到副本中的却是引用类型的引用,而不是引用的对象,在副本中对引用类型的字段值被修改后,源对象的值也将被修改。

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

当然,无论是哪种拷贝,微软都建议使用类型继承ICloneable接口的方式明确告诉调用者,该对象是否可用被拷贝。当然了,ICloneable接口只提供了一个声明为Clone的方法,我们可以根据需求在Clone的方法内实现浅拷贝或者是深拷贝,下面我们进行一段浅拷贝的案例,代码如下

1     class Student : ICloneable 2     { 3         public string IDCode { get; set; } 4  5         public int Age { get; set; } 6  7         public Grent Grent { get; set; } 8  9         #region 拷贝主体10         public object Clone()11         {12             return this.MemberwiseClone();13             //throw new NotImplementedException();14         }15         #endregion16 17     }18 19     class Grent20     {21         public string Name { get; set; }22 23         public override string ToString()24         {25             return this.Name;26         }27     }
View Code

调用

Student stu1 = new Student()            {                IDCode = "lily",                Age = 24,                Grent = new Grent() { Name="五年三班"}            };            Student stu2 = stu1.Clone() as Student;            if (stu2 == null) {                Console.WriteLine("转换失败");                Console.ReadKey();                return;            }            Console.WriteLine(stu2.IDCode);            Console.WriteLine(stu2.Age);            Console.WriteLine(stu2.Grent.ToString());            Console.WriteLine("重新为stu1赋值");            stu1.IDCode = "Anagle";            stu1.Age += 10;            stu1.Grent.Name = "六年二班";            Console.WriteLine(stu2.IDCode);            Console.WriteLine(stu2.Age);            Console.WriteLine(stu2.Grent.ToString());            Console.ReadKey();
View Code

输出结果为:

lily

24

五年三班

重新为stu1赋值

lily

24

六年二班

 

这里我们需要注意一点Student中的IDCode属性是string类型,理论上string类型是引用类型,但是由于该引用类型的特殊性,Object.MemberwiseClone方法仍旧为他创建了副本,也就是说,在浅拷贝过程中,我们应该将字符串看成值类型;

因为Student中的Grent是引用类型,所以在stu1中的Grent的Name被改变的同时,副本stu2中的Grent的Name也同样被改变。

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

[Serializable]    class Student : ICloneable    {        public string IDCode { get; set; }        public int Age { get; set; }        public Grent Grent { get; set; }        #region 拷贝主体        ///         /// 深度拷贝        ///         /// 
public Student DeepClone() { using (Stream objectStream = new MemoryStream()) { IFormatter formatter = new BinaryFormatter(); formatter.Serialize(objectStream, this); objectStream.Seek(0,SeekOrigin.Begin); return formatter.Deserialize(objectStream) as Student; } } public object Clone() { return this.MemberwiseClone(); } #endregion }
View Code

调用DeepClone方法

Student stu1 = new Student()            {                IDCode = "lily",                Age = 24,                Grent = new Grent() { Name="五年三班"}            };            Student stu2 = stu1.DeepClone() as Student;            if (stu2 == null) {                Console.WriteLine("转换失败");                Console.ReadKey();                return;            }            Console.WriteLine(stu2.IDCode);            Console.WriteLine(stu2.Age);            Console.WriteLine(stu2.Grent.ToString());            Console.WriteLine("重新为stu1赋值");            stu1.IDCode = "Anagle";            stu1.Age += 10;            stu1.Grent.Name = "六年二班";            Console.WriteLine(stu2.IDCode);            Console.WriteLine(stu2.Age);            Console.WriteLine(stu2.Grent.ToString());            Console.ReadKey();
View Code

输出结果为:

lily

24

五年三班

重新为stu1赋值

lily

24

五年三班

 

这次我们看到的结果已经很明显了,深拷贝后,源对象的重新赋值与修改将不再导致副本对象被修改,这样将很好的控制住引用类型的拷贝问题。

 

 

转载于:https://www.cnblogs.com/xufei/p/copyDeep.html

你可能感兴趣的文章
Google CFO 辞职信
查看>>
POJ2771_Guardian of Decency(二分图/最大独立集=N-最大匹配)
查看>>
Scala深入浅出实战经典之 List伴生对象操作方法代码实战.
查看>>
php 批量处理post数据
查看>>
RESTful架构详解(转)
查看>>
xcode 在哪里新建category、protocol等文件
查看>>
flash flex 程序出现错误 Error #2032
查看>>
【数据库】主键、外键、索引
查看>>
C#解析HTML
查看>>
导出/打印项目数据报表需要设置IE浏览器
查看>>
8个强大的基于Bootstrap的CSS框架
查看>>
MAC OSX在视图port哪个程序占用,杀死进程的方法
查看>>
Linux中select poll和epoll的区别
查看>>
图像识别引擎-引擎收集知识地图~
查看>>
【面试】如何找到迷宫出口
查看>>
iscroll5实现下拉加载更多
查看>>
hdu1753()模拟大型实景数字相加
查看>>
Cocos2d-x之MenuItem
查看>>
Esper学习之六:EPL语法(二)
查看>>
流和文件
查看>>