話談c#拷貝


  c#中類型分為值類型和引用類型,值類型對象賦值是本身就是賦的自身的一個副本,而引用類型賦值時則賦的是指向堆上的內存,假如我們不想賦這個地址而想將對象賦過去要怎么做呢?首先要知道拷貝分為淺表拷貝和深層拷貝,淺表拷貝得到一個新的實例,一個與原始對象類型相同、值類型字段相同的拷貝。但是,如果字段是引用類型的,則拷貝的是該引用, 而不是的對象。若想將引用字段的對象也拷貝過去,則稱為深拷貝。 為了實現拷貝,本文總結了以下幾種方法。

1.首先就是最笨的方法,傳說中的“人工拷貝”,就是將引用里的所有值對象和具有值特征的string對象一一賦給新對象,這種方式代碼量過大而且維護起來相當麻煩,所以能不用就不用。

2.System.Object提供了受保護的方法 MemberwiseClone,可用來實現“淺表”拷貝。由於該方法標記為“受保護”級別,因此,我們只能在繼承類或該類內部才能訪問該方法。

 public class A
    {
        public string rr { get; set; }
        public string tt { get; set; }


        public A ShallowCopy()
        {
            return (A)this.MemberwiseClone();
        }

    }

3.使用序列化與反序列化的方式,這種方式雖可實現深度拷貝,但有點大炮打蚊子的味道,而且在外面引用時一定要記得關閉所創建的MemoryStream

 public static object Clone(object o,out MemoryStream ms)
        {
            BinaryFormatter bf = new BinaryFormatter();
           
            ms = new MemoryStream();
            bf.Serialize(ms,o);
            ms.Seek(0, SeekOrigin.Begin);

            return bf.Deserialize(ms);
        }

4.在一個外國人寫的博客中(http://www.codeproject.com/Articles/3441/Base-class-for-cloning-an-object-in-C),使用反射的方法來解決了這個問題。他寫了一個BaseObject類,如果我們繼承這個類就可以實現深度拷貝,下面是他的實現方法:

創建一個實現 ICloneable 接口的有默認行為的抽象類,所謂的默認行為就是使用以下庫函數來拷貝類里的每一個字段。

1.遍歷類里的每個字段,看看是否支持ICloneable接口。

2.如果不支持ICloneable接口,按下面規則進行設置,也就是說如果字段是個值類型,那將其拷貝,如果是引用類型則拷貝字段指向通一個對象。

3.如果支持ICloneable,在科隆對象中使用它的克隆方法進行設置。

4.如果字段支持IEnumerable接口,需要看看它是否支持IList或者IDirectionary接口,如果支持,迭代集合看看是否支持ICloneable接口。

我們所要做的就是使得所有字段支持ICloneable接口。

下面是測試結果:

public class MyClass : BaseObject
{
    public string myStr =”test”;
    public int id;
}

public class MyContainer : BaseObject
{
    public string name = “test2”;
    public MyClass[] myArray= new MyClass[5];

    public class MyContainer()
    {
        for(int i=0 ; i<5 ; i++)
        {
             this.myArray[I] = new MyClass();
        }
    }
}
static void Main(string[] args)
{
    MyContainer con1 = new MyContainer();
    MyContainer con2 = (MyContainer)con1.Clone();

   con2.myArray[0].id = 5;
}

con2中MyClass實例中第一個元素變成了5,但con1沒有改變,即實現了深拷貝。

BaseObject的實現:

/// <summary>
/// BaseObject class is an abstract class for you to derive from.
/// Every class that will be dirived from this class will support the 
/// Clone method automaticly.<br>
/// The class implements the interface ICloneable and there 
/// for every object that will be derived <br>
/// from this object will support the ICloneable 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;
    }
}

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM