Clone 分淺拷貝和深拷貝

Clone 分淺拷貝和深拷貝

兩者的區別:當有引用類型成員時,淺拷貝複製的是成員的引用,深拷貝複製的是成員對象。

如何實現對象的拷貝功能:

== 繼承接口ICloneable實現Clone方法
== ICloneable.Clone本身並不能區分(Deep or Shallow)你可以在Clone中調用MemberwiseClone來實現一個ShallowClone也可以自己來實現一個DeepClone。不過按照microsoft的建議當實現ICloneable的時候是準備用來實現一個DeepClone。
== ICloneable.Clone返回類型是object
== 其中MemberwiseClone的複製原理是值類型按位複製,引用類型複製對象的引用。這裏有一個要注意的就是String類型,雖然是引用類型,不過在這裏表現上和值類型是一樣的,在Clone的時候就當作值類型來看待好了。


我的習慣是自己從ICloneable派生一個接口

interface IObjectCloneable : ICloneable{
  object DeepClone();
  object ShallowClone();
}


 

實現Clone的方法:

1. 手工克隆

一個能夠保證對象完全按照你所想的那樣進行克隆的方式是手工克隆對象的每一個域(field)。這種方式的缺點是麻煩而且容易出錯:如果你在類中增加了一個域,你很可能會忘記更新Clone方法。還要在克隆引用對象指向原始對象的時候,注意避免無限循環引用。下面是一個進行深拷貝的簡單例子:

public class Entity:ICloneable
    {
        private string name;
        public string Name { get; set; }
        public Entity Model;
        public object Clone()
        {
            Entity entity = new Entity();
            entity.Name = this.Name;
            if (this.Model != null)
            {
                entity.Model = (Entity)this.Model.Clone();
            }
            return entity;
        }
    }


 

2. 使用MemberWiseClone方法

MemberWiseClone是Object類的受保護方法,能夠通過創建一個新對象,並把所有當前對象中的非靜態域複製到新對象中,從而創建一個淺拷貝。對於值類型的域,進行的是按位拷貝。對於引用類型的域,引用會被賦值而引用的對象則不會。因此,原始對象及其克隆都會引用同一個對象。注意,這種方法對派生類都是有效的,也就是說,你只需在基類中定義一次Clone方法。下面是一個簡單的例子:

public class Entity:ICloneable
    {
        private string name;
        public string Name { get; set; }
        public Entity Model;
        public object Clone()
        {
            return this.MemberwiseClone();
        }
    }


 

3. 用反射進行克隆

用反射進行克隆是使用Activator.CreateInstance方法來創建一個相同類型的新對象,然後用反射對所有域進行淺拷貝。這種方法的優點是它是全自動的,不需要在對象中添加或刪除成員的時候修改克隆方法。另外它也能被寫成提供深拷貝的方法。缺點是使用了反射,因此會比較慢,而且在部分受信任的環境中是不可用的。示例代碼

4. 使用序列化進行克隆

克隆一個對象的最簡單的方法是將它序列化並立刻反序列化爲一個新對象。和反射方法一樣,序列化方法是自動的,無需在對對象成員進行增刪的時候做出修改。缺點是序列化比其他方法慢,甚至比用反射還慢,所有引用的對象都必須是可序列化的(Serializable)。另外,取決於你所使用的序列化的類型(XML,SOAP,二進制)的不同,私有成員可能不能像期望的那樣被克隆。示例代碼在這裏這裏這裏

5. 使用IL進行克隆

一種罕見的解決方案是使用IL(中間語言)來進行對象克隆。這種方式創建一個動態方法(DynamicMethod),獲取中間語言生成器(ILGenerator),向方法中注入代碼,把它編譯成一個委託,然後執行這個委託。委託會被緩存,因此中間語言只在初次克隆的時候纔會生成,後續的克隆都不會重新生成一遍。儘管這種方法比使用反射快,但是這種方法難以理解和維護。示例代碼

6. 使用擴展方法進行克隆

Havard Stranden用擴展方法(extention method)創建了一個自定義的克隆框架。這個框架能夠創建對象及其引用的對象的深拷貝,不管對象結構有多複雜。缺點是,這是一個不提供源代碼的自定義框架(更新:現在已經包括源代碼了,參見本文評論),並且它不能在不使用無參數構造器的時候,拷貝由私有方法創建的對象。另一個問題,也是所有自動化的深克隆方法共有的問題是,深拷貝通常需要靈活地處理不能進行簡單自動化特殊情況

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章