C#中實現任意類的完美複製

來源:互聯網
上載者:User

簡介

雖然在現實世界中的複製課題是有爭議的, 在.NET世界使用它卻足夠安全, 難道不是嗎?

為實現一個類你究竟有多少次要實現ICloneable介面, 而且每一次都寫相同的代碼,或為每個類寫特定的代碼。而且,當你的類加入一個新的欄位時,往往會忘記更新這個新欄位的複製方法。如果我沒說錯的話,這種時候往往會帶來惱人的bugs。

這是我的類得以存在的原因。 藉由反射機制的小小協助,我建立了一個用預設行為實現了ICloneable介面的抽象類別。現在或許你正在問自己: 什麼是預設行為? 那麼我很高興你這樣詢問。 複製的預設行為,是採用以下的規則來複製類中的每一個欄位:

  • 查看一下類中的每一個欄位是否支援ICloneable介面
    如果某欄位不支援ICloneable介面,那麼該欄位將以常規方式處理。這意味著,如果該欄位是一個實值型別,那麼該值被拷貝;如果該欄位是一個參考型別,複製的欄位將指向同一個對象。
    如果該欄位支援ICloneable介面,我們將使用其本身的Clone方法對其進行複製。
    如果該欄位支援IEnumerable介面,我們需要檢查他是否支援IList 或 IDictionary 介面。如果支援,那麼我們迭代該集件,並且查看集合的每一項是否支援Cloneable介面。

如何使用

讓你的類支援Icloneable介面所要做的就是,將你的類繼承自如下所述的BaseObject類:

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(); } } } 

現在在Main方法中加入如下代碼:

static void Main(string[] args) { MyContainer con1 = new MyContainer(); MyContainer con2 = (MyContainer)con1.Clone(); con2.myArray[0].id = 5; } 

當監測con2執行個體時,你將會看到MyClass執行個體的第一項已經變為5,而con1執行個體卻沒有改變。這樣你將明白加入到類中的任意支援ICloneable介面的欄位將被同樣地複製。而且,如果該欄位支援IList 或 IDictionary 介面,複製方法將偵測該欄位,輪詢所有項,並同樣地試圖對他們進行複製。

BaseObject類的完整實現代碼

/// <summary>/// BaseObject類是一個用來繼承的抽象類別。 /// 每一個由此類繼承而來的類將自動支援複製方法。/// 該類實現了Icloneable介面,並且每個從該對象繼承而來的對象都將同樣地/// 支援Icloneable介面。 /// </summary> public abstract class BaseObject : ICloneable{    /// <summary>        /// 複製對象,並返回一個已複製對象的引用        /// </summary>        /// <returns>引用新的複製對象</returns>         public object Clone()    {        //首先我們建立指定類型的一個執行個體                 object newObject = Activator.CreateInstance(this.GetType());        //我們取得新的類型執行個體的欄位數組。                 FieldInfo[] fields = newObject.GetType().GetFields();        int i = 0;        foreach (FieldInfo fi in this.GetType().GetFields())        {            //我們判斷欄位是否支援ICloneable介面。                         Type ICloneType = fi.FieldType.GetInterface("ICloneable", true);            if (ICloneType != null)            {                //取得對象的Icloneable介面。                                 ICloneable IClone = (ICloneable)fi.GetValue(this);                //我們使用複製方法給欄位設定新值。                                fields[i].SetValue(newObject, IClone.Clone());            }            else            {                // 如果該欄位部支援Icloneable介面,直接設定即可。                                 fields[i].SetValue(newObject, fi.GetValue(this));            }            //現在我們檢查該對象是否支援IEnumerable介面,如果支援,                         //我們還需要枚舉其所有項並檢查他們是否支援IList 或 IDictionary 介面。                        Type IEnumerableType = fi.FieldType.GetInterface("IEnumerable", true);            if (IEnumerableType != null)            {                //取得該欄位的IEnumerable介面                                IEnumerable IEnum = (IEnumerable)fi.GetValue(this);                Type IListType = fields[i].FieldType.GetInterface("IList", true);                Type IDicType = fields[i].FieldType.GetInterface("IDictionary", true);                int j = 0;                if (IListType != null)                {                    //取得IList介面。                                         IList list = (IList)fields[i].GetValue(newObject);                    foreach (object obj in IEnum)                    {                        //查看當前項是否支援支援ICloneable 介面。                                                 ICloneType = obj.GetType().GetInterface("ICloneable", true);                        if (ICloneType != null)                        {                            //如果支援ICloneable 介面,                             //我們用它李設定列表中的對象的複製                             ICloneable clone = (ICloneable)obj;                            list[j] = clone.Clone();                        }                        //注意:如果列表中的項不支援ICloneable介面,那麼                                              //在複製列表的項將與原列表對應項相同                                              //(只要該類型是參考型別)                                                j++;                    }                }                else if (IDicType != null)                {                    //取得IDictionary 介面                                        IDictionary dic = (IDictionary)fields[i].GetValue(newObject);                    j = 0;                    foreach (DictionaryEntry de in IEnum)                    {                        //查看當前項是否支援支援ICloneable 介面。                                                 ICloneType = de.Value.GetType().                            GetInterface("ICloneable", true);                        if (ICloneType != null)                        {                            ICloneable clone = (ICloneable)de.Value;                            dic[de.Key] = clone.Clone();                        }                        j++;                    }                }            }            i++;        }        return newObject;    }}

註:本文來自網路收集。

《完》

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.