首页 > 技术文章 > 对象的深拷贝-序列化拷贝

yplong 2016-03-17 18:23 原文

简介:

System.Object 几乎是所有的类、结构、委托类型的基类。System.Object有一个MemberwiseClone 的方法来帮助我们创建一个当前对象的实例。

存在的问题:

System.Object 提供的MemberwiseClone 方法只是对象的浅拷贝,只能把当前对象的非静态字段拷贝到新对象。如果属性是值类型,那么就把值拷贝一份,如果是引用类型,那么只拷贝对原对象的引用。这就意味着MemberwiseClone 不能够创建对象的深拷贝。

解决方案:

有很多实现深拷贝的方式,在这里我只介绍其中的2种。

1.通过序列化和反序列化实现深拷贝。

2.通过反射来实现深拷贝。

今天我就先介绍第一种:序列化和反序列化实现深拷贝。

ICloneable 接口提供给我们一个可以自定义实现拷贝的Clone方法。

序列化就是把对象转换成字节流的过程,反序列化相反,就是把字节流转换成原对象的过程。在.NET中有很多序列化的方式,比如二进制序列化、XML序列化等等。二进制序列化要比XML序列化快得多。所以二进制序列化是比较好的序列化和反序列化。

通过序列化和反序列化,能够实现对一个对象的深拷贝,但是前提是需要序列化和反序列化的类都要标记为serializable特性。

下面的例子,我创建一个职员类,拥有一个部门属性。

1 [Serializable]
2 public class Department
3 {
4     public int DepartmentId { get; set; }
5     public string DepartmentName { get; set; }
6 }
 1 [Serializable]
 2 public class Employee : ICloneable
 3 {
 4     public int EmployeeId { get; set; }
 5     public string EmployeeName { get; set; }
 6     public Department Department { get; set; }
 7  
 8     public object Clone()
 9     {
10         using (MemoryStream stream = new MemoryStream())
11         {
12             if (this.GetType().IsSerializable)
13             {
14                 BinaryFormatter formatter = new BinaryFormatter();
15                 formatter.Serialize(stream, this);
16                 stream.Position = 0;
17                 return formatter.Deserialize(stream);
18             }
19             return null;
20         }
21     }
22 }

看到我们用二进制序列化和反序列化实现了IClone接口的Clone方法。这个Clone方法我们可以提出来作为一个扩展方法:

 1 public static class ObjectExtension
 2 {
 3     public static T CopyObject<T>(this object objSource)
 4     {
 5         using (MemoryStream stream = new MemoryStream())
 6         {
 7             BinaryFormatter formatter = new BinaryFormatter();
 8             formatter.Serialize(stream, objSource);
 9             stream.Position = 0;
10             return (T)formatter.Deserialize(stream);
11         }
12     }
13 }

看源代码:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.IO;
 6 using System.Runtime.Serialization.Formatters.Binary;
 7 
 8 namespace SerializeCopy
 9 {
10     [Serializable]
11     public class Department
12     {
13         public int DepartmentId { get; set; }
14 
15         public string DepartmentName { get; set; }
16     }
17 
18     [Serializable]
19     public class Employee : ICloneable
20     {
21         public int EmployeeId { get; set; }
22         public string EmployeeName { get; set; }
23         public Department Department { get; set; }
24 
25         public Object Clone()
26         {
27 
28             return ObjectExtension.CopyObject<Employee>(this);
29         }
30 
31     }
32  public static class ObjectExtension
33     {
34 
35         public static T CopyObject<T>(this object objsource)
36         {
37             using (MemoryStream stream = new MemoryStream())
38             {
39                 BinaryFormatter formatter = new BinaryFormatter();
40 
41                 formatter.Serialize(stream, objsource);
42                 stream.Position = 0;
43 
44                 return (T)formatter.Deserialize(stream);
45 
46             }
47 
48         }
49 
50     }
51 
52 }

调用代码:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace SerializeCopy
 7 {
 8     class Program
 9     {
10         static void Main(string[] args)
11         {
12             Employee emp = new Employee();
13 
14             emp.EmployeeId = 1000;
15             emp.EmployeeName = "IT少年";
16             emp.Department = new Department { DepartmentId = 1, DepartmentName = "Examination" };
17 
18             Employee empclone = emp.Clone() as Employee;
19 
20             emp.EmployeeId = 1003;
21             emp.EmployeeName = "TTT";
22             emp.Department.DepartmentId = 3;
23             emp.Department.DepartmentName = "admin";
24    Console.WriteLine("----emp原始对象------");
25             Console.WriteLine("拷贝前DepartmentName应该是admin:  " + emp.Department.DepartmentName);
26             Console.WriteLine("拷贝前DepartmentID应该是3:  " + emp.Department.DepartmentId);
27 
28             Console.WriteLine("----empclone拷贝对象------");
29             Console.WriteLine("拷贝DepartmentName应该是Examination:    " + empclone.Department.DepartmentName);
30             Console.WriteLine("拷贝DepartmentID应该是1:     " + empclone.Department.DepartmentId);
31             Console.ReadKey();
32         }
33     }
34 }

调用结果:

需要注意的一点是,用序列化和反序列化深拷贝,需要将需要拷贝的属性标记为Serializable

推荐阅读