C#浅拷贝和深拷贝

C#中的对象拷贝问题;
所谓的对象拷贝,其实就是为对象创建副本,C#中将拷贝分为两种,分别为浅拷贝和深拷贝;
所谓浅拷贝就是将对象中的所有字段复制到新的副本对象中;浅拷贝对于值类型与引用类型的方式有区别,值类型字段的值被复制到副本中后,在副本中的修改不会影响源对象对应的值;然而对于引用类型的字段被复制到副本中的却是引用类型的引用,而不是引用的对象,在副本中对引用类型的字段值被修改后,源对象的值也将被修改。
深拷贝也同样是将对象中的所有字段复制到副本对象中,但是,无论对象的值类型字段或者引用类型字段,都会被重新创建并复制,对于副本的修改,不会影响到源对象的本身;

当然,无论是哪种拷贝,微软都建议使用类型继承ICloneable接口的方式明确告诉调用者,该对象是否可用被拷贝。当然了,ICloneable接口只提供了一个声明为Clone的方法,我们可以根据需求在Clone的方法内实现浅拷贝或者是深拷贝,下面我们进行一段浅拷贝的案例,代码如下

class Student : ICloneable
    {
        public string IDCode { get; set; }
        public int Age { get; set; }
        public Grent Grent { get; set; }
        #region 拷贝主体
        public object Clone()
        {
            return this.MemberwiseClone();
            //throw new NotImplementedException();
        }
        #endregion
   }
    class Grent
    {
        public string Name { get; set; }
        public override string ToString()
        {
            return this.Name;
        }

    }

调用

Student stu1 = new Student()
            {
                IDCode = "lily",
                Age = 24,
                Grent = new Grent() { Name="五年三班"}
            };
            Student stu2 = stu1.Clone() as Student;
            if (stu2 == null) {
                Console.WriteLine("转换失败");
                Console.ReadKey();
                return;
            }
            Console.WriteLine(stu2.IDCode);
            Console.WriteLine(stu2.Age);
            Console.WriteLine(stu2.Grent.ToString());
            Console.WriteLine("重新为stu1赋值");
            stu1.IDCode = "Anagle";
            stu1.Age += 10;
            stu1.Grent.Name = "六年二班";
            Console.WriteLine(stu2.IDCode);
            Console.WriteLine(stu2.Age);
            Console.WriteLine(stu2.Grent.ToString());

            Console.ReadKey();

输出结果为:
lily
24
五年三班
重新为stu1赋值
lily
24

六年二班

这里我们需要注意一点Student中的IDCode属性是string类型,理论上string类型是引用类型,但是由于该引用类型的特殊性,Object.MemberwiseClone方法仍旧为他创建了副本,也就是说,在浅拷贝过程中,我们应该将字符串看成值类型;
因为Student中的Grent是引用类型,所以在stu1中的Grent的Name被改变的同时,副本stu2中的Grent的Name也同样被改变。

Student的深拷贝有多钟实现方法,最简单的方法是手动对字段诼个赋值,但是这种方法容易出错,也就是说,如果类型的字段发生变化或有增减时,那么该拷贝方法也就要发生相应的变化,所以,建议使用序列化的形式来进行深拷贝。实现代码如下

[Serializable]
    class Student : ICloneable
    {
        public string IDCode { get; set; }
        public int Age { get; set; }
        public Grent Grent { get; set; }
        #region 拷贝主体
        /// <summary>
        /// 深度拷贝
        /// </summary>
        /// <returns></returns>
        public Student DeepClone() {
            using (Stream objectStream = new MemoryStream()) {
                IFormatter formatter = new BinaryFormatter();
                formatter.Serialize(objectStream, this);
                objectStream.Seek(0,SeekOrigin.Begin);
                return formatter.Deserialize(objectStream) as Student;
            }
        }
        public object Clone()
        {
            return this.MemberwiseClone();
        }
        #endregion

    }

调用DeepClone方法

Student stu1 = new Student()
            {
                IDCode = "lily",
                Age = 24,
                Grent = new Grent() { Name="五年三班"}
            };
            Student stu2 = stu1.DeepClone() as Student;
            if (stu2 == null) {
                Console.WriteLine("转换失败");
                Console.ReadKey();
                return;
            }
            Console.WriteLine(stu2.IDCode);
            Console.WriteLine(stu2.Age);
            Console.WriteLine(stu2.Grent.ToString());
            Console.WriteLine("重新为stu1赋值");
            stu1.IDCode = "Anagle";
            stu1.Age += 10;
            stu1.Grent.Name = "六年二班";
            Console.WriteLine(stu2.IDCode);
            Console.WriteLine(stu2.Age);
            Console.WriteLine(stu2.Grent.ToString());

            Console.ReadKey();

输出结果为:
lily
24
五年三班
重新为stu1赋值
lily
24

五年三班


这次我们看到的结果已经很明显了,深拷贝后,源对象的重新赋值与修改将不再导致副本对象被修改,这样将很好的控制住引用类型的拷贝问题。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值