【转】.NET深入学习笔记(4):深拷贝与浅拷贝(Deep Copy and Shallow Copy)

今天继续利用准备WSE安全开发文章的空闲时间,完善《.NET深入学习笔记》系列(基本都是.Net重要的知识点,我都做了详细的总结,是什么、为什么、和怎么实现)。想必很多人也接触过这两个概念。做过C++的人对深浅拷贝的概念一定不会陌生。而其很多C#高级软件工程师的面试里也会问到深浅拷贝相关的问题。我今天就在总结一下,并且添加了详细的代码实现,与大家分享。一起学习一下C#的深拷贝与浅拷贝(Deep Copy and Shallow Copy)的机制。全文还是分四部分:1.基本概念  2.深拷贝与浅拷贝实现机制 3.代码实现和分析 4.总结。下面我们来进入正式的学习。

   1.基本概念:

       首先我们应该了解一下什么叫深拷贝与浅拷贝(Deep Copy and Shallow Copy)

      a.浅拷贝(Shallow Copy影子克隆):只复制对象的基本类型,对象类型,仍属于原来的引用。
      b.深拷贝(Deep Copy 深度克隆):不紧复制对象的基本类,同时也复制原对象中的对象.完全产生新对象。

      我们知道,在C++中有拷贝构造函数和拷贝赋值函数的概念。浅拷贝就是成员数据之间的一一赋值:把值赋给一一赋给要拷贝的值。但是可能会有这样的情况:对象还包含资源,这里的资源可以指堆资源,或者一个文件。当值拷贝的时候,两个对象就有用共同的资源,同时对资源可以访问,这样就会出问题。深拷贝就是用来解决这样的问题的,它把资源也赋值一次,使对象拥有不同的资源,但资源的内容是一样的。对于堆资源来说,就是在开辟一片堆内存,把原来的内容拷贝。  

    如果你拷贝的对象中引用了某个外部的内容(比如分配在堆上的数据),那么在拷贝这个对象的时候,让新旧两个对象指向同一个外部的内容,就是浅拷贝;如果在拷贝这个对象的时候为新对象制作了外部对象的独立拷贝,就是深拷贝  
   这个C#里的概念与C++类似。我们可以参考以前的概念理解。 深拷贝与浅拷贝之间的区别基本可以从定义看出。首先浅拷贝是指将对象中的数值类型的字段拷贝到新的对象中,而对象中的引用型字段则指复制它的一个引用到目标对象。如果改变目标对象中引用型字段的值他将反映在原是对象中,也就是说原始对象中对应的字段也会发生变化。

    深拷贝拷贝不同的是对于引用拷贝的处理,深拷贝将会在新对象中创建和原是对象中对应值类型的字段并且赋值。浅拷贝不会创建新引用类型,会返回相同的类型引用。深拷贝会重新创建新对象,返回新对象的引用字。C#中的观察者模式就是浅拷贝的例子。我们保留的只是对象的副本。

 

 

  2.深拷贝与浅拷贝实现机制:

 

    从上面的概念我们了解了C#深拷贝与浅拷贝(Deep Copy and Shallow Copy)的不同之处。这个也就决定了两者有不同的实现方式。

     对于值类型:
    a.浅拷贝: 通过赋值等操作直接实现,将对象中的值类型的字段拷贝到新的对象中。      
    b.深拷贝:通过赋值等操作直接实现,将对象中的值类型的字段拷贝到新的对象中。   和浅拷贝相同

    对于引用类型:
    a.浅拷贝: MemberwiseClone 方法创建一个浅副本,方法是创建一个新对象,如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用原始对象,与原对象引用同一对象。

    b.深拷贝:拷贝对象应用,也拷贝对象实际内容,也就是创建了一个新的改变新对象 不会影响到原始对象的内容 
这种情况需要为其实现ICloneable接口中提供的Clone方法。

   差别就是在对于引用类型的实现深拷贝和浅拷贝的时候的机制不同,前者是MemberwiseClone 方法实现,后者是通过继承实现ICloneable接口中提供的Clone方法,实现对象的深拷贝。

  3.代码实现和分析:

   下面我们来看看具体的代码实现部分,首先介绍的还是值类型的。

    a.值类型浅拷贝的实现。代码如下:

/// <summary>
        
///  数组的=赋值(直接拷贝),也就是引用传递,指向的是同一个地址:
        
/// </summary>
        public void MethodShallowCopyDirectly()
        {
            
int[] ArrayInt = { 0123 };

            
//所以改变其中任意一个变量的值,另一个也会被改变
            int[] NewArrayInt = ArrayInt;
            
//改变新的数组变量:
            NewArrayInt[0= 8;
            Console.WriteLine(
"数组的复制(直接拷贝),改变新数组第一值为8,原值{0},新值{1}", ArrayInt[0], NewArrayInt[0]);
        }
        
/// <summary>
        
/// ArrayInt.CopyTo,创建以个新数组,不影响原来的值
        
/// </summary>
        public void MethodShallowCopyArrayCopyTo()
        {
            
int[] ArrayInt = { 0123 };     

            
//CopyTo()方法
            int[] NewArrayInt = new int[5];//创建以个新数组,按值拷贝,不影响原来的值
            ArrayInt.CopyTo(NewArrayInt, 0);
            NewArrayInt[
0= 8;
            Console.WriteLine(
"Array.CopyTo,改变新数组第一值为8,原值{0},新值{1}", ArrayInt[0], NewArrayInt[0]);
        
        }
        
/// <summary>
        
/// Array.Copy浅拷贝,值类型的浅拷贝,创建以个新数组,按值拷贝,不影响原来的值
        
/// </summary>
        public void MethodShallowCopyArrayCopy()
        {
            
int[] ArrayInt = { 0123 };
            
//Copy()方法
            int[] NewArrayInt = new int[4];
            Array.Copy(ArrayInt, NewArrayInt, 
0);//创建以个新数组,按值拷贝,不影响原来的值
            NewArrayInt[0= 8;
            Console.WriteLine(
"Array.Copy,改变新数组第一值为8,原值{0},新值{1}", ArrayInt[0], NewArrayInt[0]);

        }
        
/// <summary>
        
/// Array.Clone(),浅拷贝
        
/// </summary>
        public void MethodShallowCopyArrayClone()
        {
            
int[] ArrayInt = { 0123 };
            
//Array Clone()方法
            int[] NewArrayInt = ArrayInt.Clone() as int[];//按值拷贝,不影响原来的值
            NewArrayInt[0= 8;
            Console.WriteLine(
"Array.Clone(),改变新数组第一值为8,原值{0},新值{1}", ArrayInt[0], NewArrayInt[0]);
        }
        
/// <summary>
        
/// .浅拷贝:(引用类型),数组中的元素是引用类型,复制的是它的一个引用,改变新拷贝会改变原对象
        
/// </summary>
        public void MethodShallowCopyStringArrayCopyTo()
        {
            
string[] sArray ="string0""string1""string2" };
            
string[] sNewArray = sArray;
            
//浅拷贝一个新对象
            sArray.CopyTo(sNewArray, 0);

            
//改变新对象的值这个时候源对象中的值也会被改变
            sNewArray[0= "FrankXuLei";
            Console.WriteLine(
"数组的浅拷贝:(引用类型),改变第一值为FrankXuLei,原值{0},新值{1}", sArray[0], sNewArray[0]);
        }
        
/// <summary>
        
///  //字符串数组的深拷贝,如果需要包含引用类型的数组的深副本,就必须迭代数组,创建新对象
        
/// </summary>
        public void MethodDeepCopyStringArray()
        {
            
string[] sArray = new string[] { "string0""string1""string2""string3" };
            
string[] sNewArray = new string[4];//迭代数组,创建新对象
            for (int i = 0; i < sArray.Length; i++)
            {
                
string sTemp = string.Empty;
                sTemp 
= sArray[i];
                sNewArray[i] 
= sTemp;
            }
            sNewArray[
0= "FrankXuLei";
            Console.WriteLine(
"数组的复制(直接拷贝),改变新数组第一值为FrankXuLei,原值{0},新值{1}", sArray[0], sNewArray[0]);
        }

       数组的=赋值(直接拷贝),也就是引用传递,指向的是同一个地址,

所以改变其中任意一个变量的值,另一个也会被改变。ArrayInt.CopyTo,创建以个新数组,改变新的数组变量不影响原来的值。Array.Copy浅拷贝,值类型的浅拷贝,创建以个新数组,按值拷贝,不影响原来的值 .浅拷贝:(引用类型),数组中的元素是引用类型,复制的是它的一个引用,改变新拷贝会改变原对象.

   b.引用类型的深拷贝实现:

    定义了以个汽车类,继承接口继承接口ICloneable

 

public class CarDeepClone : ICloneable
    {
        
//名称,引用类型
        public string _name = string.Empty;
        
//价格,值得类型
        public int _price = 0;
        
//构造函数
        public CarDeepClone()
        {
        }
        
//重载构造函数
        public  CarDeepClone(string name, int price)
        {
            _name 
= name;
            _price 
= price;
        }
        
//深拷贝,需要重新生成对象,返回的新对象的实例
        public object Clone()
        {

            
//深复制  
            CarDeepClone obj = new CarDeepClone();//重新创建 CarDeepClone的对象
            
//obj.Member=   (ClassA)Member.Clone();  
            return obj;
        }
    }

   c.引用类型的浅拷贝实现:

   浅拷贝实现的方法是this.MemberwiseClone();创建当前对象的浅副本 ,返回相同的对象引用。而深拷贝的实现代码是通过 CarDeepClone obj = new CarDeepClone();重新创建 CarDeepClone的对象。这个是两者在实现上不同的地方。

public class CarShallowClone : ICloneable
    {
        
//名称,引用类型
        public string _name = string.Empty;
        
//价格,值得类型
        public int _price = 0;
        
//构造函数
        public  CarShallowClone(string name, int price)
        {
            _name 
= name;
            _price 
= price;
        }
        
//浅拷贝,MemberwiseClone方式返回对象的浅副本
        public object Clone()
        {
            
return this.MemberwiseClone();//创建当前对象的浅副本 ,返回相同的对象引用
        }
    }

   d.客户端测试代码实现:

    包括值类型的浅拷贝和string类型数组的深拷贝的实现测试。以及对象的深拷贝和浅拷贝的测试。具体代码如下:

ValueTypeCopy _ShallowCopy = new ValueTypeCopy();
            Console.WriteLine(
"Value Type Shallow Copy Demo 值类型浅拷贝。。。。。。。。。。。。。。。。。。");
            _ShallowCopy.MethodShallowCopyDirectly();
//直接赋值
            _ShallowCopy.MethodShallowCopyArrayClone();//调用数组的Clone()方法,浅副本
            _ShallowCopy.MethodShallowCopyArrayCopy();//ArrayCopy方法
            _ShallowCopy.MethodShallowCopyArrayCopyTo();//ArrayCopyTo()方法
            _ShallowCopy.MethodShallowCopyStringArrayCopyTo();//ArrayCopyTo()方法
 
            _ShallowCopy.MethodDeepCopyStringArray();
//深拷贝字符数组

 

            
//DeepCopy Test深拷贝,重新生成对象,对新对象的修改不会改变原来对象的值
            Console.WriteLine("Object Deep    Copy Demo  对象深拷贝。。。。。。。。。。。。。。。。。。。。。");
            CarDeepClone _CarDeepClone1 
= new CarDeepClone("Benz700",700);
            
//深拷贝
            Console.WriteLine("DeepCopy Test深拷贝,原对象名字{0}", _CarDeepClone1._name);
            CarDeepClone _CarDeepClone2 
= _CarDeepClone1.Clone() as CarDeepClone;

            Console.WriteLine(
"DeepCopy Test深拷贝,新对象名字{0}", _CarDeepClone2._name);
            
//修改新对象的名字
            _CarDeepClone2._name = "Benz800";
            Console.WriteLine(
"DeepCopy Test深拷贝,新对象名字修改为{0}", _CarDeepClone2._name);
            
//输出对象信息
            Console.WriteLine("DeepCopy Test深拷贝,原对象名字为{0},新对象名字为{1}", _CarDeepClone1._name, _CarDeepClone2._name);

            
//ShallowCopy Test浅拷贝,新对象的修改会改变原来对象的值得
            Console.WriteLine("Object Shallow Copy Demo  对象浅拷贝。。。。。。。。。。。。。。。。。。。。。");
            CarShallowClone _CarShallowClone1 
= new CarShallowClone("BMW3"300);
            Console.WriteLine(
"ShallowCopy Test浅拷贝,原对象名字{0}", _CarShallowClone1._name);
            
//浅拷贝对象
            CarShallowClone _CarShallowClone2 = _CarShallowClone1.Clone() as CarShallowClone;
            Console.WriteLine(
"ShallowCopy Test浅拷贝,新对象名字{0}", _CarShallowClone2._name);
            
//修改新对象名字
            _CarShallowClone2._name = "BMW7";
            Console.WriteLine(
"ShallowCopy Test浅拷贝,新对象名字修改为{0}", _CarShallowClone2._name);
            
//输出对象信息
            Console.WriteLine("ShallowCopy Test浅拷贝,原对象名字{0},新对象名字{1}", _CarShallowClone1._name, _CarShallowClone2._name);
           

     

 

    首先测试的值类型的不同的浅拷贝方法,实例化类ValueTypeCopy _ShallowCopy = new ValueTypeCopy();
    进行 值类型浅拷测试贝。分别包括:   _ShallowCopy.MethodShallowCopyDirectly();直接赋值拷贝,
            _ShallowCopy.MethodShallowCopyArrayClone();调用数组的Clone()方法,浅副本
            _ShallowCopy.MethodShallowCopyArrayCopy();ArrayCopy方法
            _ShallowCopy.MethodShallowCopyArrayCopyTo();ArrayCopyTo()方法
            _ShallowCopy.MethodShallowCopyStringArrayCopyTo();ArrayCopyTo()方法
             _ShallowCopy.MethodDeepCopyStringArray();深拷贝字符数组
后面代码主要是对对象深浅拷贝的不同测试。

运行结果如下图:

CloneTest.gif 

4.总结

     通过以上内容的学习,希望大家对C#中的深拷贝与浅拷贝(Deep Copy and Shallow Copy)的机制能有更深入的了解。我在总结这个文章的时候也查阅了MSDN及C#书籍等资料。与大家一起分享。有问题的也可以留言一起交流~共同学习进步~(最后附上本次实现的代码。下载地址

/Files/frank_xl/CloneDemoFrankXuLei.zip 。前几天忙硕士论文的事情,没时间更新blog,现在抓紧时间继续准备WSE3.0安全开发的文章,应该最近会写出来。主要是涉及到的知识点很多,需要时间学习,而且开发环境配置比较复杂。代码插入出了问题,有js错误,请大家下载代码)

转载于:https://www.cnblogs.com/jiangj/archive/2010/08/20/1804518.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/379040.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

abap 添加alv上的工具栏的按钮_Excel里的置顶功能——快速访问工具栏

100万职场人都在看后台回复礼包领199元职场干货大家好&#xff0c;我是小可~今天跟大家分享一个提高Excel操作效率的小技巧自定义你的快速访问工具栏设置后的效果▼▼▼也就是把你最经常用到的两三个功能放到快速访问工具栏可以一眼就找到这些功能不需要靠快捷键或者功能选项卡…

用递归法求1²+2²+...+n²的值

思路分析: 谈到递归,我个人会联想到数学里面的通式。因为递归调用的函数的对应法则是相同的。例如这道题:f(x)=x。这个就是函数通式,只不过把每个求得的结果进行累加求和即可。用户输入5的时候,会出现f(5)=5,之后再进行x减一操作,执行f(4)=4,最后将每个进行累加即可。…

编写一个函数,计算下式当n=10和n=100的值。

思路分析: 首先,我个人看法:当我拿到这道题的时候,我会把它当成一道数学题对待。分子是动的,恒为一,分母是进行依次增加的。且奇数项为正,偶数项为负。因为设计运算出现的是分数,故,设计选取存储类型为double。 找出问题: ①正负号问题、②分母问题、③累计求和问题…

POJ 1001 大数的乘法

对这道题的理解 大数的乘法 关键是 实型的 那么首先就是数出来小数点有几位这个相信很简单 从后面往前数刚开始0 不算接着就是遇到小数点结束如果没有小数点 那么置为0 接着就是输出地时候首先算出小数点的位置然后输出 你想怎么样都行 从后往前数这个时候输出 那么就是你也…

铃木uy125摩托车机油_济南铃木安徽发布国四新车—6480元瑞梦125、9380元UY125

安徽合肥&#xff0c;这个具有两千多年历史的古城&#xff0c;以“三国故地、包拯家乡”而闻名海内外&#xff0c;2019年4月22日济南铃木为这座城市带来一份惊喜&#xff0c;今年正值国四执行&#xff0c;济南铃木旗下两款国四新车瑞梦125与UY125正式在合肥与大家相见。济南铃木…

编写一个程序,计算用户输入的起始时间到终止时间之间相隔的天数。

思路分析&#xff1a; 闰年&#xff1a;闰年又分为普通闰年和世纪闰年 普通闰年&#xff1a;能被4整除且不能被100整除的为闰年(2004为闰年&#xff0c;1999不是闰年) 世纪闰年&#xff1a;能被400整除的是闰年(2020年是闰年&#xff0c;1900年不是闰年) 闰年共有366天&#x…

mvc的Controller返回值类型ActionResult详解

一、简介 ActionResult 操作方法通过执行工作并返回操作结果来响应用户输入。 操作结果表示框架将代表操作方法执行的命令。 ActionResult 类是操作结果的基类。 以下类型从 ActionResult 派生&#xff1a; ContentResult EmptyResult FileResult HttpUnauthorizedResult …

栅格布局一般怎么用_建筑混凝土色差大怎么办?用这种方法处理,一般都看不出来...

由于模板锈蚀、脱模剂污染、原材料等原因&#xff0c;建筑混凝土成形后经常会遇到颜色不一致的现象&#xff0c;为此我们总结了混凝土面色差调整施工工艺&#xff0c;可供大家参考使用。一、混凝土面色差调整施工工艺流程及说明基层表面打磨→吸尘器吸尘→湿润墙面→素水泥处理…

FusionChart完全入门手册4

想不想打造让人震撼的图表系统&#xff0c;想不想做出和别人不一样的图表&#xff0c;从本节起&#xff0c;我就带领大家走入这片神奇的土地&#xff0c;让大家去采摘属于自己的创意之果&#xff0c;我们的目标是------个性无罪&#xff0c;个性万岁&#xff01; 问题三、如何做…

ffplay分析(音频解码线程的操作)

《ffplay的数据结构分析》 《ffplay分析&#xff08;从启动到读取线程的操作&#xff09;》 《ffplay分析&#xff08;视频解码线程的操作&#xff09;》 《ffplay 分析&#xff08;音频从Frame(解码后)队列取数据到SDL输出&#xff09;》 《ffplay分析 &#xff08;视频从Fram…

More Effective C++ (运算符)

4.1&#xff1a;谨慎定义类型转换函数<1>容易的方法是利用一个最新的编译器特性&#xff1a;explicit关键字<2>C编译器把">>"作为一个符号来解释&#xff0c;在两个">"间没有空格&#xff0c;语句会产生语法错误。<3>隐式类型转…

php微信获取mediaid超出限制_Python实现每日微信自动打卡

众所周知&#xff0c;因为疫情的原因&#xff0c;很多高校和公司都要求员工每日在微信上进行打卡&#xff0c;来汇报自己的当前身体状态和所处地区。但绝大多数情况下&#xff0c;每天打卡的信息其实是不会变的&#xff0c;我们要做的就是进入公众号——自动登录点进打卡页面—…

ffplay 分析(音频从Frame(解码后)队列取数据到SDL输出)

《ffplay的数据结构分析》 《ffplay分析&#xff08;从启动到读取线程的操作&#xff09;》 《ffplay分析&#xff08;视频解码线程的操作&#xff09;》 《ffplay分析&#xff08;音频解码线程的操作&#xff09;》 《ffplay分析 &#xff08;视频从Frame(解码后)队列取数据到…

pandas 根据列名索引多列数据_Pandas 数据聚合与分组运算[groupby+apply]速查笔记

利用Pandas将数据进行分组&#xff0c;并将各组进行聚合或自定义函数处理。Pandas中Groupby分组与聚合过程导入模块import pandas as pd缩写df表示Dataframe对象分组df.groupby(col1)&#xff1a; 根据col1列将df全部列分组&#xff08;默认&#xff1a;axis0行&#xff09;df[…

ffplay分析 (视频从Frame(解码后)队列取数据到SDL输出)

《ffplay的数据结构分析》 《ffplay分析&#xff08;从启动到读取线程的操作&#xff09;》 《ffplay分析&#xff08;视频解码线程的操作&#xff09;》 《ffplay分析&#xff08;音频解码线程的操作&#xff09;》 《ffplay 分析&#xff08;音频从Frame(解码后)队列取数据到…

redis 备份导出rdb_Redis数据迁移利器之redisshake

“当需要进行Redis实例或集群数据迁移时&#xff0c;我们可以采用导出/导入的方式进行数据迁移&#xff0c;但当需要做数据异地灾备或双活时&#xff0c;再使用传统的方式就不合适了&#xff0c;我们需要借助工具(如redis-port/redis-shake)来完成。”redis-shake介绍redis-sha…

线性结构常规操作(四)

定义存储结构(以单向链表为主) 对于链表的定义&#xff0c;通过结构体进行定义&#xff0c;包括两部分&#xff0c;一是数据域&#xff0c;另一个就是指针&#xff0c;用于指向下一个节点。 1&#xff0c;创建链表 定义链表&#xff1a; struct nodesq{int data;//数据域&a…

ffplay分析 (暂停 / 播放处理)

《ffplay的数据结构分析》 《ffplay分析&#xff08;从启动到读取线程的操作&#xff09;》 《ffplay分析&#xff08;视频解码线程的操作&#xff09;》 《ffplay分析&#xff08;音频解码线程的操作&#xff09;》 《ffplay 分析&#xff08;音频从Frame(解码后)队列取数据到…

源码 状态机_[源码阅读] 阿里SOFA服务注册中心MetaServer(1)

[源码阅读] 阿里SOFA服务注册中心MetaServer(1)0x00 摘要0x01 服务注册中心1.1 服务注册中心简介1.2 SOFARegistry 总体架构1.3 为什么要分层0x02 MetaServer2.1简介2.2 问题0x03 代码结构0x04 启动运行4.1 集成部署4.2 独立部署0x05 总体逻辑5.1 程序主体5.2 配置0x06 启动6.1…

HttpService远程校验

今天学了下HttpService&#xff0c;和大家分享一下。HttpService是用来读取远程数据的一个对象&#xff0c;数据格式为XML。 我做了一个登陆校验的功能&#xff0c;主要是通过HttpService将服务器端的用户数据得到&#xff0c;然后在客户端判断输入的用户名和密码是否存在。 主…