【Frank.Xu】.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.值类型浅拷贝的实现。代码如下:

   

///
        ///  数组的=赋值(直接拷贝),也就是引用传递,指向的是同一个地址:
        ///
        public void methodshallowcopydirectly()
        {
            int[] arrayint = { 0, 1, 2, 3 };

            //所以改变其中任意一个变量的值,另一个也会被改变
            int[] newarrayint = arrayint;
            //改变新的数组变量:
            newarrayint[0] = 8;
            console.writeline("数组的复制(直接拷贝),改变新数组第一值为8,原值{0},新值{1}", arrayint[0], newarrayint[0]);
        }
        ///
        /// arrayint.copyto,创建以个新数组,不影响原来的值
        ///
        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]);
       
        }
        ///
        /// array.copy浅拷贝,值类型的浅拷贝,创建以个新数组,按值拷贝,不影响原来的值
        ///
        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]);

        }
        ///
        /// array.clone(),浅拷贝
        ///
        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]);
        }
        ///
        /// .浅拷贝:(引用类型),数组中的元素是引用类型,复制的是它的一个引用,改变新拷贝会改变原对象
        ///
        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]);
        }
        ///
        ///  //字符串数组的深拷贝,如果需要包含引用类型的数组的深副本,就必须迭代数组,创建新对象
        ///
        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 bj = new cardeepclone();//重新创建 cardeepclone的对象
            //obj.member=   (classa)member.clone(); 
            return obj;
        }
    }

 

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

   浅拷贝实现的方法是this.memberwiseclone();创建当前对象的浅副本 ,返回相同的对象引用。而深拷贝的实现代码是通过 cardeepclone bj = 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,现在是抓紧时间继续准备wse安全开发的文章,应该最近会写出来。主要是涉及到的知识点很多,实现和原理了解了,但是开发环境配置比较复杂。有问题的也可以留言一起交流~共同学习进步~(代码插入出了问题,有js错误,请大家下载代码)

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/15783504/viewspace-557538/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/15783504/viewspace-557538/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1 目标检测的定义 目标检测(Object Detection)的任务是找出图像中所有感兴趣的目标(物体),确定它们的类别和位置,是计算机视觉领域的核心问题之一。由于各类物体有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具有挑战性的问题。 目标检测任务可分为两个关键的子任务,目标定位和目标分类。首先检测图像中目标的位置(目标定位),然后给出每个目标的具体类别(目标分类)。输出结果是一个边界框(称为Bounding-box,一般形式为(x1,y1,x2,y2),表示框的左上角坐标和右下角坐标),一个置信度分数(Confidence Score),表示边界框中是否包含检测对象的概率和各个类别的概率(首先得到类别概率,经过Softmax可得到类别标签)。 1.1 Two stage方法 目前主流的基于深度学习的目标检测算法主要分为两类:Two stage和One stage。Two stage方法将目标检测过程分为两个阶段。第一个阶段是 Region Proposal 生成阶段,主要用于生成潜在的目标候选框(Bounding-box proposals)。这个阶段通常使用卷积神经网络(CNN)从输入图像中提取特征,然后通过一些技巧(如选择性搜索)来生成候选框。第二个阶段是分类和位置精修阶段,将第一个阶段生成的候选框输入到另一个 CNN 中进行分类,并根据分类结果对候选框的位置进行微调。Two stage 方法的优点是准确度较高,缺点是速度相对较慢。 常见Tow stage目标检测算法有:R-CNN系列、SPPNet等。 1.2 One stage方法 One stage方法直接利用模型提取特征值,并利用这些特征值进行目标的分类和定位,不需要生成Region Proposal。这种方法的优点是速度快,因为省略了Region Proposal生成的过程。One stage方法的缺点是准确度相对较低,因为它没有对潜在的目标进行预先筛选。 常见的One stage目标检测算法有:YOLO系列、SSD系列和RetinaNet等。 2 常见名词解释 2.1 NMS(Non-Maximum Suppression) 目标检测模型一般会给出目标的多个预测边界框,对成百上千的预测边界框都进行调整肯定是不可行的,需要对这些结果先进行一个大体的挑选。NMS称为非极大值抑制,作用是从众多预测边界框中挑选出最具代表性的结果,这样可以加快算法效率,其主要流程如下: 设定一个置信度分数阈值,将置信度分数小于阈值的直接过滤掉 将剩下框的置信度分数从大到小排序,选中值最大的框 遍历其余的框,如果和当前框的重叠面积(IOU)大于设定的阈值(一般为0.7),就将框删除(超过设定阈值,认为两个框的里面的物体属于同一个类别) 从未处理的框中继续选一个置信度分数最大的,重复上述过程,直至所有框处理完毕 2.2 IoU(Intersection over Union) 定义了两个边界框的重叠度,当预测边界框和真实边界框差异很小时,或重叠度很大时,表示模型产生的预测边界框很准确。边界框A、B的IOU计算公式为: 2.3 mAP(mean Average Precision) mAP即均值平均精度,是评估目标检测模型效果的最重要指标,这个值介于0到1之间,且越大越好。mAP是AP(Average Precision)的平均值,那么首先需要了解AP的概念。想要了解AP的概念,还要首先了解目标检测中Precision和Recall的概念。 首先我们设置置信度阈值(Confidence Threshold)和IoU阈值(一般设置为0.5,也会衡量0.75以及0.9的mAP值): 当一个预测边界框被认为是True Positive(TP)时,需要同时满足下面三个条件: Confidence Score > Confidence Threshold 预测类别匹配真实值(Ground truth)的类别 预测边界框的IoU大于设定的IoU阈值 不满足条件2或条件3,则认为是False Positive(FP)。当对应同一个真值有多个预测结果时,只有最高置信度分数的预测结果被认为是True Positive,其余被认为是False Positive。 Precision和Recall的概念如下图所示: Precision表示TP与预测边界框数量的比值 Recall表示TP与真实边界框数量的比值 改变不同的置信度阈值,可以获得多组Precision和Recall,Recall放X轴,Precision放Y轴,可以画出一个Precision-Recall曲线,简称P-R
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值