zoukankan      html  css  js  c++  java
  • .NET对象克隆

    < 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写的一篇好文章吸引了我, 他使用该方法提供一个好的克隆实现。 这篇文章讨论得非常好! 以下是链接:
    http://www.codeproject.com/csharp/cloneimpl_class.asp

    上面讨论的任何一个方法,都要求对象的成员类型能支持自我克隆,以确保“深层”克隆能成功进行。也就是说, 对象必须是可串行化的(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;
       }
    }
    }

  • 相关阅读:
    Window 窗口类
    使用 Bolt 实现 GridView 表格控件
    lua的table库
    Windows编程总结之 DLL
    lua 打印 table 拷贝table
    使用 xlue 实现简单 listbox 控件
    使用 xlue 实现 tips
    extern “C”
    COleVariant如何转换为int double string cstring
    原来WIN32 API也有GetOpenFileName函数
  • 原文地址:https://www.cnblogs.com/netcorner/p/2912136.html
Copyright © 2011-2022 走看看