`

深度拷贝与浅度拷贝

 
阅读更多

 

深度拷贝与浅度拷贝

881人阅读 评论(0) 收藏 举报

今天继续利用准备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 = { 0, 1, 2, 3 };

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

            //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 = { 0, 1, 2, 3 };
            //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 = { 0, 1, 2, 3 };
            //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;
                sNewArray = 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();深拷贝字符数组后面代码主要是对对象深浅拷贝的不同测试。
运行结果如下图:
  
4.总结
     通过以上内容的学习,希望大家对C#中的深拷贝与浅拷贝(Deep Copy and Shallow Copy)的机制能有更深入的了解。我在总结这个文章的时候也查阅了MSDN及C#书籍等资料。与大家一起分享。有问题的也可以留言一起交流~共同学习进步~(最后附上本次实现的代码。下载地址
/Files/frank_xl/CloneDemoFrankXuLei.zip 。前几天忙硕士论文的事情,没时间更新blog,现在抓紧时间继续准备WSE3.0安全开发的文章,应该最近会写出来。主要是涉及到的知识点很多,需要时间学习,而且开发环境配置比较复杂。代码插入出了问题,有js错误,请大家下载代码)

分享到:
评论

相关推荐

    c#深度复制浅度复制

    c#深度复制浅度复制

    Java中的深拷贝(深复制)和浅拷贝(浅复制)介绍

    主要介绍了Java中的深拷贝(深复制)和浅拷贝(浅复制)介绍,需要的朋友可以参考下

    C#中深度复制和浅度复制详解

    本文章主要是讲解C# 语言编程中,深度复制和浅度复制,下面我将通过一个实例进行讲解。在实例开发之前,我们得先知道深度复制是什么和浅度复制是什么,它们之间的区别又是什么,下面将为大家一一揭晓。 1.深度复制是...

    解析JAVA深度克隆与浅度克隆的区别详解

    本篇文章是对JAVA深度克隆与浅度克隆的区别进行了详细的分析介绍,需要的朋友参考下

    ASP.NET深度复制和浅度复制分析

    之前一直没有搞清楚深度复制和浅度复制的区别到底在哪里,今天彻底把这个东西弄懂了,写出来与到家共勉。 如果大家不懂值类型和引用类型的区别,请先看//www.jb51.net/article/57471.htm,本来想自己写的,但刚好...

    C#浅拷贝和深拷贝实例解析

    主要介绍了C#浅拷贝和深拷贝,是比较重要的概念,需要的朋友可以参考下

    网络语言浅度研究报告.doc

    网络语言浅度研究报告.doc

    基于java实现浅度爬虫应用

    基于java实现浅度爬虫应用,是我学习java来练练手的,java爬虫基础入门的学生可以考虑参考一下

    浅度探索C++对象模型bin3.ppt

    浅度探索C++对象模型bin3.ppt

    JRE Hack 浅度研究

    NULL 博文链接:https://lzy.iteye.com/blog/407956

    MyEclipse浅度优化方法

    大家是否觉得MyEclipse启动很慢,那么来看一下吧,如果谁有其他的优化方法,欢迎交流!

    浅度解析C++运算符重载

    Scott Meyers的著作都一直堪称经典,如《More Effective C++》、《Effective STL》,但由于我憋脚的英语,因此一直与佛无缘。现在好了,有Lostmouse、lians、save 等前辈把这些著作翻译为中文版,让我们这些晚生...

    实例详解vue.js浅度监听和深度监听及watch用法

    主要介绍了vue.js浅度监听和深度监听及watch用法,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友参考下吧

    南理文字Simple Three

    南理文字RPG Three 此次代码增加了 1.百位NPC比赛,决出金牌、银牌、铜牌...浅拷贝顾名思义便只是浅度地拷贝,对被拷贝对象如果有引用,直接把引用对象地引用给拷贝,被拷贝对象地改变将引起拷贝对象地改变 item = {

    javascript克隆对象深度介绍

    克隆或者拷贝分为2种: 浅度克隆:基本类型为值传递,对象仍为引用传递。 深度克隆:所有元素或属性均完全克隆,并于原引用类型完全独立,即,在后面修改对象的属性的时候,原对象不会被修改。 代码如下: function ...

    prototypeAndCreate.zip

    原型实例代码示例 浅度克隆、深度克隆 创建模式代码示例 组装车辆

    .NET原型模式讲解

    创建型模式中一个比较特殊的模式-原型模式,有个最大的特点是克隆一个现有的对象,这个克隆的结果有2种,一种是浅度复制,另一种是深度复制。 创建型模式一般是用来创建一个新的对象,然后我们使用这个对象完成一些...

Global site tag (gtag.js) - Google Analytics