• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
cacotopia
You can make it here,you can make it anywhere.
博客园    首页    新随笔    联系   管理    订阅  订阅

C#中Object的深拷贝

简介:

C#中System.Object 是所有类类型、结构类型、枚举类型和委托类型的基类。可以说它是类型继承的基础。System.Object包括一个用于创建当前对象实例的一份拷贝的MemberwiseClone的成员方法。

问题描述:

System.Object的MemberwiseClone方法创建一个新对象的浅拷贝,并把当前对象实例的非静态字段拷贝至新对象实例中。通过属性,对象拷贝能够正确执行:如果属性是值类型,那么将按位拷贝数据,如果属性是引用类型,那么将拷贝原始对象的引用,也就是说,克隆对象指向同一个对象实例。这就意味着MemberwiseClone方法并未创建一个对象的深拷贝。

解决方法:

有许多方法实现类对象的深拷贝,下面我将通过示例介绍其中的两种:

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

  2.通过反射实现深拷贝

1、通过序列化、反序列化实现对象的深拷贝

ICloneable接口使得开发者定制实现Clone方法用以创建一个已存对象的深拷贝。通常Object.MemberwiseClone方法帮助开发者创建一份已存对象的拷贝,但是创建的是这个对象的浅拷贝。序列化是指将对象状态存储为二进制流的过程,而反序列化是指将而二进制流转换为原始对象的过程。在.Net中有许多方法可以实现序列化和反序列化,例如二进制序列化、XML序列化、数据约定序列化等等。二进制序列化比XML序列化快速,且二进制序列化使用私有、公有字段,因而二进制序列化是实现序列化和反序列化的不错选择。

通过序列化和反序列化,可以创建对象的深拷贝。需要注意的是,所有类型只有标记为[serializable]特性才能实现序列化和反序列化。

示例程序:

首先创建Employee类,它包含Department类型的属性,Employee类继承ICloneable接口并且实现Clone方法。运用二进制格式化器(binary formater),实现对象的序列化和反序列化为一个新对象。

 1 [Serializable]
 2     public class Department
 3     {
 4         private Int32 _DepartmentID;
 5         private String _DepartmentName;
 6         public Int32 DepartmentID 
 7         {
 8             get {return _DepartmentID;}
 9             set { _DepartmentID = value;}
10         }
11         public String DepartmentName
12         {
13             get {return _DepartmentName;}
14             set {_DepartmentName = value;}
15         }
16     }
17      [Serializable]
18     public class Employee : ICloneable
19     {
20           private Int32 _EmployeeID;
21           private String _EmployeeName;
22           private Department _Dempartment;
23         public Int32 EmployeeID 
24         { 
25             get {return _EmployeeID;} 
26             set {_EmployeeID = value; }
27         }
28         public String EmployeeName 
29         { 
30             get { return _EmployeeName; }
31             set { _EmployeeName = value;} 
32         }
33         public Department Department 
34         { 
35             get{ return _Dempartment; } 
36             set{ _Dempartment= value;} 
37         }
38 
39         public object Clone()
40         {
41             using (MemoryStream stream = new MemoryStream())
42             {
43                 if (this.GetType().IsSerializable)
44                 {
45                     BinaryFormatter formatter = new BinaryFormatter();
46                     formatter.Serialize(stream, this);
47                     stream.Position = 0;
48                     return formatter.Deserialize(stream);
49                 }
50                 return null;
51             }
52         }
53     }
View Code

也可以通过扩展方法实现:

 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     }

2.通过反射实现深拷贝

反射用于获取运行时对象原始信息。运用System.Reflection名字空间的类可以获取运行史对象时的信息,从已存对象创建类型实例,并访问其属性及调用方法。考虑一下代码,我创建了一个接受Object参数的静态方法,并且返回同样类型的一个新实例。

 1  public class Utility
 2     {
 3         public static object CloneObject(object objSource)
 4         {
 5             //Get the type of source object and create a new instance of that type
 6             Type typeSource = objSource.GetType();
 7             object objTarget = Activator.CreateInstance(typeSource);
 8 
 9             //Get all the properties of source object type
10             PropertyInfo[] propertyInfo = typeSource.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
11 
12             //Assign all source property to taget object 's properties
13             foreach (PropertyInfo property in propertyInfo)
14             {
15                 //Check whether property can be written to 
16                 if (property.CanWrite)
17                 {
18                     //check whether property type is value type, enum or string type
19                     if (property.PropertyType.IsValueType || property.PropertyType.IsEnum || property.PropertyType.Equals(typeof(System.String)))
20                     {
21                         property.SetValue(objTarget, property.GetValue(objSource, null), null);
22                     }
23                     //else property type is object/complex types, so need to recursively call this method until the end of the tree is reached
24                     else
25                     {
26                         object objPropertyValue = property.GetValue(objSource, null);
27                         if (objPropertyValue == null)
28                         {
29                             property.SetValue(objTarget, null, null);
30                         }
31                         else
32                         {
33                             property.SetValue(objTarget, CloneObject(objPropertyValue), null);
34                         }
35                     }
36                 }
37             }
38             return objTarget;
39         }
40     }
View Code

这也可以通过以下的扩展方法实现。

 1  public static class ObjectExtension
 2     {
 3         public static object CloneObject(this object objSource)
 4         {
 5             //Get the type of source object and create a new instance of that type
 6             Type typeSource = objSource.GetType();
 7             object objTarget = Activator.CreateInstance(typeSource);
 8 
 9             //Get all the properties of source object type
10             PropertyInfo[] propertyInfo = typeSource.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
11 
12             //Assign all source property to taget object 's properties
13             foreach (PropertyInfo property in propertyInfo)
14             {
15                 //Check whether property can be written to 
16                 if (property.CanWrite)
17                 {
18                     //check whether property type is value type, enum or string type
19                     if (property.PropertyType.IsValueType || property.PropertyType.IsEnum || property.PropertyType.Equals(typeof(System.String)))
20                     {
21                         property.SetValue(objTarget, property.GetValue(objSource, null), null);
22                     }
23                     //else property type is object/complex types, so need to recursively call this method until the end of the tree is reached
24                     else
25                     {
26                         object objPropertyValue = property.GetValue(objSource, null);
27                         if (objPropertyValue == null)
28                         {
29                             property.SetValue(objTarget, null, null);
30                         }
31                         else
32                         {
33                             property.SetValue(objTarget, objPropertyValue.CloneObject(), null);
34                         }
35                     }
36                 }
37             }
38             return objTarget;
39         }
40     }
View Code

以下是示例代码及输出:

 1 class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Employee emp = new Employee();
 6             emp.EmployeeID = 1000;
 7             emp.EmployeeName = "Cacotopia";
 8             emp.Department = new Department { DepartmentID = 1, DepartmentName = "development center" };
 9 
10             Employee empClone = emp.Clone() as Employee;
11 
12             Employee empClone1 = Utility.CloneObject(emp) as Employee;
13             Employee empClone2 = emp.CloneObject() as Employee;
14 
15             Employee empClone3 = emp.CopyObject<Employee>();
16             //now Change Original Object Value
17             emp.EmployeeName = "24/7";
18             emp.Department.DepartmentName = "Admin";
19 
20             //Print origianl as well as clone object properties value.
21 
22             Console.WriteLine("Original Employee Name : " + emp.EmployeeName);
23             Console.WriteLine("Original Department Name : " + emp.Department.DepartmentName);
24 
25             Console.WriteLine("");
26 
27             Console.WriteLine("Clone Object Employee Name (Clone Method) : " + empClone.EmployeeName);
28             Console.WriteLine("Clone Object Department Name (Clone Method) : " + empClone.Department.DepartmentName);
29 
30             Console.WriteLine("");
31 
32             Console.WriteLine("Clone Object Employee Name (Static Method) : " + empClone1.EmployeeName);
33             Console.WriteLine("Clone Object Department Name (Static Method) : " + empClone1.Department.DepartmentName);
34 
35             Console.WriteLine("");
36 
37             Console.WriteLine("Clone Object Employee Name (Extension Method) : " + empClone2.EmployeeName);
38             Console.WriteLine("Clone Object Department Name (Extension Method) : " + empClone2.Department.DepartmentName);
39             Console.WriteLine("press any key to exit...");
40             Console.ReadKey();
41         }
42     }
View Code

结束语:

通过序列化和反射我们可以实现对象的深拷贝。运用序列化实现深拷贝的唯一不足之处在于必须将对象标记为Serializable。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2013-09-28 21:36  cacotopia  阅读(2819)  评论(1)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3