2013-06-05 197 views
0

我寫了一些代碼,發現兩個類(即下面的Fish和Mammal)具有相同的模式,所以我決定總結泛型。我應該實施ICloneable嗎?

問題是,我需要複製基類類構造函數。

此外,由於構造函數不是默認(0參數),所以無法使用new()約束(CS0304)修復此問題。

我寫這個是因爲我被告知實施ICloneable不是一個好的做法。

public class OneHeart {...} 
public class TwoHeart {...} 

public class Animal<TyHeart> /* : ICloneable ?*/ { 
    public Animal(TyHeart heart) { 
     if(heart==null) throw new ArgumentNullException(); 
     Heart = heart; 
    } 
    public Heart { get; set; } 
} 
public class Fish : Animal<OneHeart> { 
    public Fish(OneHeart heart) : base(heart) {} 
    public Fish(Fish fish) : base(fish.Heart) {} // copy ctor but no use? 
} 
public class Mammal : Animal<TwoHeart> { 
    public Mammal(TwoHeart heart, Organ lung) : base(heart) {Lungs=lung;} 
    public Mammal(Mammal mammal) : base(mammal.Heart) {Lungs=mammal.Lung;} 
    public Organ Lungs {get; set;} // Mammal-only member:) 
} 
// The Zoo collects animals of only one type: 
public class Zoo<TyHeart, TyAnimal> : LinkedList<TyAnimal> 
    where TyAnimal : Animal<TyHeart> { 
    public Zoo() : base() {} 
    public Zoo(Zoo<TyHeart, TyAnimal> srcZoo) { 
     foreach(var animal in srcZoo) { 
      // CS0304 compile error: 
      base.AddLast(new TyAnimal(animal)); 
     } 
    } 
    ... 
} 

魚類和哺乳動物來自動物性和唯一的類

我知道他們都實現拷貝構造函數。

編輯:沒有深層複製需要。

(類型)心臟和肺是單身和動物/魚/哺乳動物共享。

回答

1

與實施ICloneable的一個問題是一個可變引用類型的存儲位置(字段,變量,數組槽等)可被用於封裝身份,可變的狀態,兩者的,或兩者都不是,但既不.NET也不它的任何語言都包含任何標準約定,以指示上面哪個給定的存儲位置應該封裝。

如果一個字段Foo1封裝身份,但沒有可變的狀態,然後George.Clone().Foo1應該是指同一個實例George.Foo1

如果一個字段Foo2封裝了可變的狀態,但不認同,那麼George.Clone().Foo2應該指的是被初始化爲具有相同的狀態George.Foo2一個新的對象。

如果字段Foo3既沒有包含可變狀態也沒有標識[即只有不同於身份的不可變狀態],那麼George.Clone().Foo3可能會引用George.Foo3,具有相同狀態的新對象或具有相同狀態的任何其他便利對象。

如果字段Foo4封裝了可變狀態和標識,則使用Clone方法不能有效克隆該類型的實例。

如果存在封裝身份,可變狀態,兩者或兩者都不同的存儲位置類型,那麼深度與淺度的差別幾乎沒有,因爲任何引用類型字段或其他存儲位置都被封裝類型應該通過遞歸地應用上述規則來克隆(注意,對於正確構造的對象,這不會導致無限遞歸,因爲兩個對象不能有意義地封裝彼此的可變狀態而沒有其中至少一個也封裝另一個的身份)。不幸的是,因爲類型系統中不存在這樣的區別,所以除了實現臨時克隆方法外,沒有實際的解決方案,或者實現使用屬性決定應該做什麼的克隆方法,並且使用硬編碼行爲來構建 - 不包含任何特殊指標的.NET類型。

0

默認的基礎構造函數將被調用,如果在派生的構造函數聲明中給出base(...)不同的話。

您不必實施ICloneable,因爲它可能會使您對問題的深度或是淺拷貝造成麻煩。但無論如何,您可以按照克隆模式(僅以不同的方式調用它)。

以下是可以引導您使用或不使用ICloneable接口來實施克隆設施的資源:How to Implement ICloneable Interface in Derivable Classes in .NET

這個想法是創建一個除默認構造函數外的受保護的副本構造函數。這樣可以讓你保持new()約束,並且仍然提供派生類,並提供了在需要時克隆基本內容和派生內容的選項。

+0

可繼承類不應具有不會鏈接到MemberwiseClone的克隆成員。否則,在不覆蓋它的派生類的實例上調用'Clone'將產生錯誤類型的實例。 – supercat

+0

@supercat是不是所有虛擬方法的情況?暫時忘掉ICloneable接口,想象您實現了自己的克隆機制(例如Copy方法)。這個方法自然會返回對基類型的引用。任何繼承者將不得不重寫它,否則它將無法正常工作。依賴MemberwiseClone不能解決深度克隆的問題。 –

+0

如果克隆成員形成可返回到'MemberwiseClone'的鏈(最好使用受保護的虛擬方法鏈接),並且派生類不創建任何私有的可變對象,則父類的克隆方法將正確地創建派生類型的實例。否則,每個派生類都必須定義一個克隆方法覆蓋,即使它唯一的事情是鏈接到父代的拷貝構造函數,如果不這樣做不會導致編譯器診斷 - 只是破壞行爲。 – supercat

相關問題