对象的浅挎贝和深挎贝

一、概念

      把一个对象复制为另一个对象,有深挎贝(也叫深表复制,deep copy)和浅挎贝(也叫浅表复制,shallow copy)之分,而两种方式在值类型的对象和引用类型的对象中又有所区别。对于值类型对象而言,无论是深挎贝还是浅挎贝,效果都是同样的——即复制对象的值本身到新对象中,对原对象的值的修改不会影响到新对象。而对于引用对象而言,如果是浅挎贝,则只是复制对象的引用,因此,对原对象的值的修改,将会影响到新对象的值,因为新对象和原对象的引用都指向同一个内存位置;而如果是深挎贝,则是将原对象的值(不是引用)复制一份,并使新对象的引用指向它,因此,对原对象值的修改,不会影响到新对象中的值,因为两者已经指向不同的位置。

 

二、实现

      对于值对象来说,深挎贝和浅挎贝可以通过赋值操作(“=”运算符)来实现。对于值对象而言,浅挎贝的实现一般通过Object类的MemberwiseClone方法来完成;深挎贝则有两种不同的方法:

      (1)创建一个新的对象,并对这个对象的所有成员依次赋值;

      (2)使用内存流来序列化和反序列化对象。

 

      以下是第(2)种示例的代码:

 

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;


namespace ConsoleApplication1
{
    [Serializable]
    class A : ICloneable
    {
        public int i = 0;
        public int[] arr = { 1, 2, 3 };

        /// <summary>
        /// 使用MemberwiseClone执行浅挎贝
        /// </summary>
        /// <returns></returns>
        public A Clone1()
        {
            //调用MemberwiseClone创建一个浅表副本,具体来说就是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。
            //如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象
            return this.MemberwiseClone() as A;
        }

        /// <summary>
        /// 执行深挎贝
        /// </summary>
        /// <returns></returns>
        public A Clone2()
        {
            //创建内存存储区
            MemoryStream memoryStream = new MemoryStream();

            //以二进制的格式序列化对象为流
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            binaryFormatter.Serialize(memoryStream, this);

            memoryStream.Position = 0;

            //反序列化
            return binaryFormatter.Deserialize(memoryStream) as A;

            //其他深挎贝的方式,是创建一个新的对象,并依次给这个对象赋值
        }

        public object Clone()
        {
            return this.MemberwiseClone();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.i = 10;
            a.arr = new int[]{8,9,10};

            A b1 = a.Clone1();
            A b2 = a.Clone2();

            Console.WriteLine("a.i={0},b1.i={1},b2.i={2}", a.i.ToString(), b1.i.ToString(), b2.i.ToString());
            Console.WriteLine("a.arr[0]={0},b1.arr[0]={1},b2.arr[0]={2}", a.arr[0].ToString(), b1.arr[0].ToString(), b2.arr[0].ToString());

            //修改原始类a的值
            a.i = 20;
            a.arr[0] = 11;

            //对于a修改后,浅挎贝后,b1类中,值类型i的值未变,值为10;引用类型的arr的值发生改变,为11;
            //深挎贝后,b2类中值类型的值未变,i-10;引用类值的值也未改变,为arr[0]=8。
            Console.WriteLine("shallow copy,a.i={0},b1.i={1}", a.i.ToString(), b1.i.ToString());
            Console.WriteLine("shallow copy,a.arr[0]={0},b1.arr[0]={1}", a.arr[0].ToString(), b1.arr[0].ToString());
            Console.WriteLine("deep copy,a.i={0},b2.i={1}", a.i.ToString(), b2.i.ToString());
            Console.WriteLine("deep copy,a.arr[0]={0},b2.arr[0]={1}", a.arr[0].ToString(), b2.arr[0].ToString());

            Console.ReadLine();
           
        }
    }   
}

三、应用

1、对于值类型来讲,建议都不需要支持ICloneable接口,使用默认的赋值操作就可以了;

2、对于引用类型来说,如果要选择实现ICloneable接口,则要明确实现类中是实现了深挎贝还是浅挎贝;

3、当在一个类中实现ICloneable接口时,此类的每一个派生类(包括派生类的派生类)都必须显式地实现Clone方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值