【转】.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,一经查实,立即删除!

相关文章

c语言数组-1_C数组-智能问题与解答

c语言数组-1C programming Arrays (One-D Array, Two-D Array) Aptitude Questions and Answers : In this section you will find C Aptitude Questions and Answers on One Dimensional (1D) and Two Dimensional (2D) array. C编程数组(一维数组&#xff0c;二维数组)能力问…

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

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

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

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

机器学习资料推荐

机器学习资料推荐 机器学习的资料 1:斯坦福大学视频(作为入门教程&#xff0c;网易有中文字幕&#xff0c;而且讲义也有翻译版本&#xff09;20集200左右讲义 2&#xff1a;机器学习 Tom M.Mitchell(虽然出版10多年&#xff0c;但是通俗易懂的内容&#xff0c;让读者对机器学习…

ffplay源码(版本:ffmpeg-4.2.1)

ffplay源码&#xff08;版本&#xff1a;ffmpeg-4.2.1&#xff09; /** Copyright (c) 2003 Fabrice Bellard** This file is part of FFmpeg.** FFmpeg is free software; you can redistribute it and/or* modify it under the terms of the GNU Lesser General Public* Lic…

stringwriter_Java StringWriter toString()方法与示例

stringwriterStringWriter类的toString()方法 (StringWriter Class toString() method) toString() method is available in java.io package. toString()方法在java.io包中可用。 toString() method is used to represent the buffer current value in terms of string. toStr…

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

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

POJ 1001 大数的乘法

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

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

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

Andrej Karpathy最新大模型入门视频讲解

最近这两天&#xff0c;特斯拉前AI总监 现在在OpenAI的安德烈卡帕西&#xff08;Andrej Karpathy&#xff09;的新教程火了 这次 他专门面向普通大众做了一个关于大语言模型的科普视频 时长1个小时&#xff0c;全部为“非技术介绍”&#xff0c; 涵盖了模型推理、训练、微…

Jquery 寻找父、子、兄弟节点

JQUERY的父&#xff0c;子&#xff0c;兄弟节点查找方法 jQuery.parent(expr) 找父亲节点&#xff0c;可以传入expr进行过滤&#xff0c;比如$("span").parent()或者$("span").parent(".class") jQuery.parents(expr),类似于jQuery.parents(exp…

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

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

longvalue_Java Short类longValue()方法及示例

longvalue短类longValue()方法 (Short class longValue() method) longValue() method is available in java.lang package. longValue()方法在java.lang包中可用。 longValue() method is used to return the value denoted by this Short object converted to type long (by …

mvc的Controller返回值类型ActionResult详解

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

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

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

Java SimpleTimeZone equals()方法与示例

SimpleTimeZone类的equals()方法 (SimpleTimeZone Class equals() method) equals() method is available in java.util package. equals()方法在java.util包中可用。 equals() method is used to check whether this SimpleTimeZone and the given object (ob) are equals or …

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>隐式类型转…

大纲(二)

一、数据结构就是逻辑结构存储结构(物理结构)相应操作(算法实现) 二、逻辑结构 集合1:1 线性结构1:n 树m:n 图 主要是可以画到纸上进行分析的结构图就是逻辑结构&#xff0c;分析问题可以得出唯一一个逻辑结构 三、存储结构(物理结构) 顺序存储结构(例如:线性表)链式存储结…