一般情況下,我們不建議針對類型實現ICloneable介面。因為如果一個類型支援ICloneable介面,那麼該類型的所有衍生類別都必須實現它,而且類型中所有成員類型也都要實現ICloneable介面,或者有其他建立複製對象的機制,當我們設計的類型包含交織成網狀的對象時,支援深複製會變得比較複雜。
複製操作分為淺複製和深複製兩種,其中淺複製是指新對象包含所有成員變數的副本,如果成員變數為參考型別,那麼新對象將和原對象引用同樣的對象;深複製中新對象也包含所有成員變數的副本,但是所有參考型別的成員變數都會被遞迴的複製。對於C#來說,內建類型,例如整型,執行深複製和淺複製的結果是相同的。
任何只包含內建類型成員的實值型別都不需要實現ICloneable介面,一個簡單的指派陳述式要比Clone()方法效率更高,因為Clone()方法必須對傳回值進行裝箱,才能轉換成一個System.Object引用。
而對於參考型別來說,如果參考型別需要通過實現ICloneable介面的方式來表明自身支援淺複製或者深複製,這時,該類型的衍生類別也必須實現ICloneable介面。
來看下面的代碼。
代碼
1 class BaseType : ICloneable
2 {
3 private string _label = "class name";
4 private int [] _values = new int [ 10 ];
5
6 public object Clone()
7 {
8 BaseType rVal = new BaseType( );
9 rVal._label = _label;
10 for( int i = 0; i < _values.Length; i++ )
11 rVal._values[ i ] = _values[ i ];
12 return rVal;
13 }
14 }
15
16 class Derived : BaseType
17 {
18 private double [] _dValues = new double[ 10 ];
19
20 static void Main( string[] args )
21 {
22 Derived d = new Derived();
23 Derived d2 = d.Clone() as Derived;
24
25 if ( d2 == null )
26 Console.WriteLine( "null" );
27 }
上述代碼在運行後,我們會發現d2的值時null,這是因為Derived類從BaseType類繼承了Clone()方法,但是繼承來的實現對Derived類型來說是不正確的,因為它只是複製了基類,BaseType.Clone()方法建立了一個BaseType類型的對象,而不是一個Derived類型的對象。
如果需要解決這個問題,我們可以將BaseType類中的Clone()方法聲明為abstract,這樣所有的衍生類別都必須實現這個方法。另外,我們還可以通過以下的方式來解決這個問題。
代碼
1 class BaseType
2 {
3 private string _label;
4 private int [] _values;
5
6 protected BaseType( )
7 {
8 _label = "class name";
9 _values = new int [ 10 ];
10 }
11
12 // Used by devived values to clone
13 protected BaseType( BaseType right )
14 {
15 _label = right._label;
16 _values = right._values.Clone( ) as int[ ] ;
17 }
18 }
19
20 sealed class Derived : BaseType, ICloneable
21 {
22 private double [] _dValues = new double[ 10 ];
23
24 public Derived ( )
25 {
26 _dValues = new double [ 10 ];
27 }
28
29 // Construct a copy
30 // using the base class copy ctor
31 private Derived ( Derived right ) :
32 base ( right )
33 {
34 _dValues = right._dValues.Clone( )
35 as double[ ];
36 }
37
38 static void Main( string[] args )
39 {
40 Derived d = new Derived();
41 Derived d2 = d.Clone() as Derived;
42 if ( d2 == null )
43 Console.WriteLine( "null" );
44 }
45
46 public object Clone()
47 {
48 Derived rVal = new Derived( this );
49 return rVal;
50 }
51 }
總之,對於實值型別來講,我們永遠都不需要實現ICloneable介面,使用預設的賦值操作就可以了,我們應該為那些確實需要複製操作的”葉子類“實現ICloneable介面,對於那些子類可能需要實現ICloneable介面的基類來說,我們應該為其建立一個受保護的複製建構函式。除此之外,我們應該避免實現ICloneable介面。