C#中浅拷贝与深拷贝(复制)

 在有些时候,我们需要从数据库读取数据填充对象或从硬盘读取文件填充对象,但是这样做相对耗时。这时候我们就想到了对象的拷贝。本文即以实例形式解析了C#浅拷贝和深拷贝的用法。

 

       C#中有两种类型变量,一种 是值类型变量,一种是引用类型变量。对于前者,copy是属于全盘复制;而对后者,一般的copy只是浅copy,相当于只传递一个引用指针一样。因此 对于后者进行真正copy的时候,也是最费事的,具体的说,必须为其实现ICloneable接口中提供的Clone方法。

一、浅拷贝

1.什么是"浅拷贝":

当针对一个对象浅拷贝的时候,对于对象的值类型成员,会复制其本身,对于对象的引用类型成员,仅仅复制对象引用,这个引用指向托管堆上的对象实例。

例如:有一个对象,包含引用类型的类成员和值类型的struct成员

  即:Cinema包含 引用类型成员Room和值类型成员Film。

 
  1. public class Room

  2. {

  3. public int _maxSeat;

  4. public Room(int maxSeat)

  5. {

  6. this._maxSeat = maxSeat;

  7. }

  8. }

  9. public struct Film

  10. {

  11. public string _name;

  12. public Film(string name)

  13. {

  14. this._name = name;

  15. }

  16. }

  17. public class Cinema

  18. {

  19. public Room _room;

  20. public Film _film;

  21. public Cinema(Room room, Film film)

  22. {

  23. this._room = room;

  24. this._film = film;

  25. }

  26. public object Clone()

  27. {

  28. return MemberwiseClone(); //对引用类型实施浅复制

  29. }

  30. }

3.测试拷贝后的效果

①打印出原先对象  拷贝前值类型和引用类型成员的值 
②对原先对象拷贝,打印出复制对象值类型和引用类型成员的值 
③改变原先对象的值,再次打印原先对象的值类型和引用类型成员的值 
④再次打印复制对象值类型和引用类型成员的值

 
  1. static void Main(string[] args)

  2. {

  3. Room room1 = new Room(60);

  4. Film film1 = new Film("家园防线");

  5. Cinema cinema1 = new Cinema(room1, film1);

  6. Cinema cinema2 = (Cinema)cinema1.Clone();

  7. Console.WriteLine("拷贝之前,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name,cinema1._room._maxSeat);

  8. Console.WriteLine("拷贝之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat);

  9. //修改拷贝之前引用类型的字段值

  10. cinema1._film._name = "极品飞车";

  11. cinema1._room._maxSeat = 80;

  12. Console.WriteLine("修改之后,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name, cinema1._room._maxSeat);

  13. Console.WriteLine("修改之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat);

  14. Console.ReadKey();

  15. }

运行结果如下:

分析:

浅拷贝关键点是对引用类型拷贝的是对象引用,这个引用指向托管堆上的对象实例。改变原对应引用类型的值,会影响到复制对象。

二、深拷贝

1.什么是"深拷贝"

对引用成员指向的对象也进行复制,在托管堆上赋值原先对象实例所包含的数据,再在托管堆上创建新的对象实例。

2.通过对每个对象成员进行复制进行深拷贝

 
  1. public object Clone()

  2. {

  3. Room room = new Room();

  4. room._maxSeat = this._room._maxSeat;//复制当前引用类型成员的值到新对象 </span>

  5. Film film = this._film; //值类型直接赋值

  6. Cinema cinema = new Cinema(room, film);

  7. return cinema;

  8. }

3.也可以通过序列化和反序列化进行深拷贝

 
  1. public object Clone1()

  2. {

  3. BinaryFormatter bf = new BinaryFormatter();

  4. MemoryStream ms = new MemoryStream();

  5. bf.Serialize(ms, this); //复制到流中

  6. ms.Position = 0;

  7. return (bf.Deserialize(ms));

4.采用序列化和反序列化深拷贝,但必须把所有的类打上[Serializable],测试代码如下:

 
  1. [Serializable]

  2. public class Room

  3. {

  4. public int _maxSeat;

  5. public Room()

  6. {}

  7. public Room(int maxSeat)

  8. {

  9. this._maxSeat = maxSeat;

  10. }

  11. }

  12. [Serializable]

  13. public struct Film

  14. {

  15. public string _name;

  16. public Film(string name)

  17. {

  18. this._name = name;

  19. }

  20. }

  21. [Serializable]

  22. public class Cinema

  23. {

  24. public Room _room;

  25. public Film _film;

  26. public Cinema(Room room, Film film)

  27. {

  28. this._room = room;

  29. this._film = film;

  30. }

  31. //浅拷贝

  32. //public object Clone()

  33. //{

  34. // return MemberwiseClone(); //对引用类型实施浅复制

  35. //}

  36. //深拷贝 对每个对象成员进行复制

  37. public object Clone()

  38. {

  39. Room room = new Room();

  40. room._maxSeat = this._room._maxSeat;//复制当前引用类型成员的值到新对象

  41. Film film = this._film; //值类型直接赋值

  42. Cinema cinema = new Cinema(room, film);

  43. return cinema;

  44. }

  45. //使用序列化和反序列化进行复制

  46. public object Clone1()

  47. {

  48. BinaryFormatter bf = new BinaryFormatter();

  49. MemoryStream ms = new MemoryStream();

  50. bf.Serialize(ms, this); //复制到流中

  51. ms.Position = 0;

  52. return (bf.Deserialize(ms));

  53. }

  54. }

5.测试拷贝后的效果

 
  1. static void Main(string[] args)

  2. {

  3. Room room1 = new Room(60);

  4. Film film1 = new Film("家园防线");

  5. Cinema cinema1 = new Cinema(room1, film1);

  6. Cinema cinema2 = (Cinema)cinema1.Clone1();

  7. Console.WriteLine("拷贝之前,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name,cinema1._room._maxSeat);

  8. Console.WriteLine("拷贝之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat);

  9. //修改拷贝之前引用类型的字段值

  10. cinema1._film._name = "极品飞车";

  11. cinema1._room._maxSeat = 80;

  12. Console.WriteLine("修改之后,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name, cinema1._room._maxSeat);

  13. Console.WriteLine("修改之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat);

  14. Console.ReadKey();

  15. }

结果:

分析:

深拷贝后,两个对象的引用成员已经分离,改变原先对象引用类型成员的值

并不会对复制对象的引用类型成员值造成影响。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C#的序列化和反序列化是将对象转换为二进制数据或将二进制数据转换为对象的过程。在C#,可以使用BinaryFormatter、XmlSerializer、DataContractSerializer等多种方式进行序列化和反序列化。 深拷贝浅拷贝用于复制对象。浅拷贝复制对象的引用,而不会复制对象的实际内容。深拷贝则会完全复制对象的内容,包括引用类型的成员变量。 在C#,可以通过实现ICloneable接口来实现浅拷贝。对于深拷贝,则需要自己手动实现一个递归复制对象的方法。 下面是一个示例代码,演示了如何进行序列化、反序列化以及深拷贝浅拷贝: ```csharp using System; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Xml.Serialization; [Serializable] public class Person : ICloneable { public string Name { get; set; } public int Age { get; set; } public Address Address { get; set; } public object Clone() { return new Person { Name = Name, Age = Age, Address = (Address)Address?.Clone() }; } } [Serializable] public class Address : ICloneable { public string City { get; set; } public string Street { get; set; } public object Clone() { return new Address { City = City, Street = Street }; } } public static class SerializationHelper { public static byte[] SerializeToBinary(object obj) { using (var ms = new MemoryStream()) { var formatter = new BinaryFormatter(); formatter.Serialize(ms, obj); return ms.ToArray(); } } public static T DeserializeFromBinary<T>(byte[] data) { using (var ms = new MemoryStream(data)) { var formatter = new BinaryFormatter(); return (T)formatter.Deserialize(ms); } } public static string SerializeToXml(object obj) { using (var writer = new StringWriter()) { var serializer = new XmlSerializer(obj.GetType()); serializer.Serialize(writer, obj); return writer.ToString(); } } public static T DeserializeFromXml<T>(string xml) { using (var reader = new StringReader(xml)) { var serializer = new XmlSerializer(typeof(T)); return (T)serializer.Deserialize(reader); } } } public static class ObjectHelper { public static T ShallowCopy<T>(this T obj) { return (T)obj.MemberwiseClone(); } public static T DeepCopy<T>(this T obj) { using (var ms = new MemoryStream()) { var formatter = new BinaryFormatter(); formatter.Serialize(ms, obj); ms.Position = 0; return (T)formatter.Deserialize(ms); } } } // 示例代码 var person1 = new Person { Name = "Tom", Age = 30, Address = new Address { City = "Beijing", Street = "Chang'an Avenue" } }; // 深拷贝 var person2 = person1.DeepCopy(); person2.Name = "Jerry"; person2.Address.City = "Shanghai"; Console.WriteLine($"person1.Name={person1.Name}, person1.Address.City={person1.Address.City}"); Console.WriteLine($"person2.Name={person2.Name}, person2.Address.City={person2.Address.City}"); // 浅拷贝 var person3 = person1.ShallowCopy(); person3.Name = "John"; person3.Address.City = "Guangzhou"; Console.WriteLine($"person1.Name={person1.Name}, person1.Address.City={person1.Address.City}"); Console.WriteLine($"person3.Name={person3.Name}, person3.Address.City={person3.Address.City}"); // 序列化和反序列化 var data = SerializationHelper.SerializeToBinary(person1); var person4 = SerializationHelper.DeserializeFromBinary<Person>(data); Console.WriteLine($"person1.Name={person1.Name}, person1.Address.City={person1.Address.City}"); Console.WriteLine($"person4.Name={person4.Name}, person4.Address.City={person4.Address.City}"); var xml = SerializationHelper.SerializeToXml(person1); var person5 = SerializationHelper.DeserializeFromXml<Person>(xml); Console.WriteLine($"person1.Name={person1.Name}, person1.Address.City={person1.Address.City}"); Console.WriteLine($"person5.Name={person5.Name}, person5.Address.City={person5.Address.City}"); ``` 输出结果: ``` person1.Name=Tom, person1.Address.City=Beijing person2.Name=Jerry, person2.Address.City=Shanghai person1.Name=Tom, person1.Address.City=Guangzhou person3.Name=John, person3.Address.City=Guangzhou person1.Name=Tom, person1.Address.City=Beijing person4.Name=Tom, person4.Address.City=Beijing person1.Name=Tom, person1.Address.City=Beijing person5.Name=Tom, person5.Address.City=Beijing ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值