博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
.NET对象克隆
阅读量:5961 次
发布时间:2019-06-19

本文共 5067 字,大约阅读时间需要 16 分钟。

< DOCTYPE html PUBLIC -WCDTD XHTML StrictEN httpwwwworgTRxhtmlDTDxhtml-strictdtd>
大家一定知道.NET对象是有二大类型的: 值类型和引用类型。 值类型对象的变量表示对象本身,而且具有“copy-on-assignment”的行为。也就是说, 以下的讨论不适用于值类型。
另一方面,引用类型的变量实际上是指向堆上的内存。 因此,如果你创建了一个引用类型的变量,并且将一个已存在的对象分配给它,实际上是创建了指向堆上的相同内存的另外一个对象。本文就是讨论这样的情况:创建的一个对象的新的拷贝,并且保存在一个变量中!
为什么要克隆?
我认为当设置一个对象的状态要付出昂贵的代价,并且又需要取得该对象的一个拷贝以便改变当前的一些状态时,克隆就显得十分必要。下面列举一个刚好能体现我刚刚所说的情况的例子。 就拿 DataTable 类来说吧。建立一个 DataTable 会包含诸如以下的操作:为取得架构和数据而查询数据库、添加约束、设置主键等等。那么,当需要该 DataTable 的一个新的拷贝,哪怕是对架构作极小的改变或添加新的一行记录等等, 明智的选择会是克隆已存在的对象再对其进行操作,而不是创建一个新的DataTable, 那样将需要更多的时间和资源。

克隆也广泛应用于数组和集合,这些时候往往会多次需要已存在对象的一个拷贝。

克隆的类型
我们基于克隆的程度将克隆分成两大类:“深层”克隆和“浅表”克隆。“浅表”克隆得到一个新的实例,一个与原始对象类型相同、包含值类型字段的拷贝。但是,如果字段是引用类型的, 该引用将被拷贝, 而不是拷贝引用的对象。 因此,原始对象的引用和克隆对象的引用都指向同一个对象。另一方面, 对象的“深层”克隆包含原始对象直接或间接引用的对象的所有拷贝。下面举例说明。

对象X引用对象A,对象A引用对象M。对象X的“浅表”克隆对象Y,同样也引用了对象A。相对比的是,对象X的“深层”克隆对象Y,却直接引用了对象B,并且间接引用对象N,这里,对象B是对象A的拷贝,对象N是对象M的拷贝。

实现克隆
System.Object提供了受保护的方法 MemberwiseClone,可用来实现“浅表”克隆。由于该方法标记为“受保护”级别,因此,我们只能在继承类或该类内部才能访问该方法。
.NET定义了一个IClonable接口,一个需要“深层”克隆而不是“浅表”克隆的类必须实现该接口。我们需要提供一个好的实现方法来达到该接口的Clone方法实现的功能。
有许多方法可以实现“深层”克隆。一个方法是将对象串行化到内存流中,然后反串行化到一个新的对象。我们将使用一个二进制(Binary)的 Formatter类或SOAP formatter类来进行深层串行化。做一个深写成连载长篇而刊登的 formatter 。 这个方法的问题是类和它的成员 (完整的类表) 必须被标记为serializable,否则formatter会发生错误。
反射是另外一个能达到相同目的的方法。 Amir Harel写的一篇好文章吸引了我, 他使用该方法提供一个好的克隆实现。 这篇文章讨论得非常好! 以下是链接:

上面讨论的任何一个方法,都要求对象的成员类型能支持自我克隆,以确保“深层”克隆能成功进行。也就是说, 对象必须是可串行化的(serializable) ,或者每个独立的成员必须提供IClonable的实现。 如果不这样,我们根本不可能对对象进行“深层”克隆!

综述
克隆是提供给程序员的一个很好的方法。但是, 我们应该知道什么时候需要提供这样的功能,而且在某些情况下,严格地说,对象不应该提供这一个特性。 以SQLTransaction 类为例, 就不支持克隆。这一个类代表了SQL Server数据库的一个事务。 克隆该对象没有任何意义,因为我们可能不能够理解一个数据库的一个活动的事务的克隆! 因此,如果你认为克隆对象的状态会产生应用程序逻辑上的矛盾,就不需要支持克隆。

示例代码:

using System;

using System.Reflection;
using System.Collections;

namespace Amir_Harel.Cloning

{
/// <summary>
/// <b>BaseObject</b> class is an abstract class for you to derive from. <br>
/// Every class that will be dirived from this class will support the <b>Clone</b> method automaticly.<br>
/// The class implements the interface <i>ICloneable</i> and there for every object that will be derived <br>
/// from this object will support the <i>ICloneable</i> interface as well.
/// </summary>
public abstract class BaseObject : ICloneable
{
   /// <summary>
   /// Clone the object, and returning a reference to a cloned object.
   /// </summary>
   /// <returns>Reference to the new cloned object.</returns>
   public object Clone()
   {
    //First we create an instance of this specific type.
    object newObject = Activator.CreateInstance( this.GetType() );

    //We get the array of fields for the new type instance.

    FieldInfo[] fields = newObject.GetType().GetFields();

    int i = 0;

   
    foreach( FieldInfo fi in this.GetType().GetFields() )
    {    
     //We query if the fiels support the ICloneable interface.
     Type ICloneType = fi.FieldType.GetInterface( "ICloneable" , true );

     if( ICloneType != null )

     {
      //Getting the ICloneable interface from the object.
      ICloneable IClone = (ICloneable)fi.GetValue(this);

      //We use the clone method to set the new value to the field.

      fields[i].SetValue( newObject , IClone.Clone() );
     }
     else
     {
      //If the field doesn't support the ICloneable interface then just set it.
      fields[i].SetValue( newObject , fi.GetValue(this) );
     }

     //Now we check if the object support the IEnumerable interface, so if it does

     //we need to enumerate all its items and check if they support the ICloneable interface.
     Type IEnumerableType = fi.FieldType.GetInterface( "IEnumerable" , true );
     if( IEnumerableType != null )
     {
      //Get the IEnumerable interface from the field.
      IEnumerable IEnum = (IEnumerable)fi.GetValue(this);

      //This version support the IList and the IDictionary interfaces to iterate

      //on collections.
      Type IListType = fields[i].FieldType.GetInterface( "IList" , true );
      Type IDicType = fields[i].FieldType.GetInterface( "IDictionary" , true );

      int j = 0;

      if( IListType != null )
      {
       //Getting the IList interface.
       IList list = (IList)fields[i].GetValue(newObject);
      
       foreach( object obj in IEnum )
       {
        //Checking to see if the current item support the ICloneable interface.
        ICloneType = obj.GetType().GetInterface( "ICloneable" , true );

        if( ICloneType != null )

        {
         //If it does support the ICloneable interface, we use it to set the clone of
         //the object in the list.
         ICloneable clone = (ICloneable)obj;

         list[j] = clone.Clone();        

        }
       
        //NOTE: If the item in the list is not support the ICloneable interface then
        // in the cloned list this item will be the same item as in the original list
        //(as long as this type is a reference type).

        j++;

       }
      }
      else if( IDicType != null )
      {
       //Getting the dictionary interface.
       IDictionary dic = (IDictionary)fields[i].GetValue(newObject);
       j = 0;
       foreach( DictionaryEntry de in IEnum )
       {
        //Checking to see if the item support the ICloneable interface.
        ICloneType = de.Value.GetType().GetInterface( "ICloneable" , true );

        if( ICloneType != null )

        {
         ICloneable clone = (ICloneable)de.Value;

         dic[de.Key] = clone.Clone();        

        }
        j++;
       }
      }
     }
     i++;   
    }
    return newObject;
   }
}
}

本文转自 netcorner 博客园博客,原文链接:http://www.cnblogs.com/netcorner/archive/2008/06/06/2912136.html  ,如需转载请自行联系原作者

你可能感兴趣的文章
Swift游戏开发之俄罗斯方块:No.5 Block Party
查看>>
hdu 5280 Senior&#39;s Array
查看>>
剑指offer系列之十八:顺时针打印矩阵
查看>>
不借助第三方插件利用ScrollView自身delegate实现下拉刷新和上拉加载
查看>>
如何设置Docker容器中Java应用的内存限制
查看>>
推荐几款工具
查看>>
深入浅出: 大小端模式
查看>>
深入浅出: Java回调机制(异步)
查看>>
Aliyun OSS Nginx proxy module(阿里云OSS Nginx 签名代理模块)
查看>>
linux中的mdev机制
查看>>
use zfs snapshot rollback postgresql's primary to old status in PG HA
查看>>
btrfs 使用指南 - 1 概念,创建,块设备管理,性能优化
查看>>
Android Studio 3.0 上 Gradle 改动
查看>>
[Vue]1-5. Vue.js核心知识之组件化
查看>>
链表(二)
查看>>
重学前端之 让人心态爆炸的this到底是个什么玩意
查看>>
阿里云服务器ECS 3年 279元
查看>>
lamp组合详解
查看>>
Android 自定义View基础(一)
查看>>
新锐时代(北京)网络科技有限公司拖欠工资
查看>>