• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

C#中Object的深拷贝

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

简介:

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。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C#的static与单例模式发布时间:2022-07-10
下一篇:
C#Socket初探发布时间:2022-07-10
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap