zoukankan      html  css  js  c++  java
  • 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。

  • 相关阅读:
    VC++学习(16):线程同步与异步套接字
    VC++学习(15):多线程
    VC++学习(12):文件操作
    VC++学习(10):绘图控制
    VC++学习(13):文档串行化
    VC++学习(11):图形的保存和重绘
    VC++学习(18):Active控件
    四大数据库的比较(SQL Server、Oracle、Sybase和DB2)
    Gridview中二级联动
    VS 2008中PDA开发环境的相关配置
  • 原文地址:https://www.cnblogs.com/cacotopia/p/miracle_20130928.html
Copyright © 2011-2022 走看看