程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Effective C#原則27:避免使用ICloneable(1)

Effective C#原則27:避免使用ICloneable(1)

編輯:關於C語言

ICloneable看上去是個不錯的主意:為一個類型實現ICloneable接口後就可 以支持拷貝了。如果你不想支持拷貝,就不要實現它。

但你的對象並不 是在一個“真空”的環境中運行,但考慮到對派生類的些影響,最好 還是對ICloneable支持。一但某個類型支持ICloneable, 那麼所有的派生類都必 須保持一致,也就是所有的成員必須支持ICloneable接口或者提供一種機制支持 拷貝。最後,支持深拷貝的對象,在創建設計時如果包含有網絡結構的對象,會 使拷貝很成問題。ICloneable也覺察到這個問題,在它的官方定義中有說明:它 同時支持深拷貝和淺拷貝。淺拷貝是創建一個新的對象,這個新對象對包含當前 對象中所有成員變量的拷貝。如果這些成員變量是引用類型的,那麼新的對象與 源對象包含了同樣的引用。而深拷貝則可以很好的拷貝所有成員變量,引用類型 也被遞歸的進行了拷貝。對於像整型這樣的內置類型,深拷貝和淺拷貝是一樣的 結果。哪一種是我們的類型應該支持的呢?這取決於類型本身。但同時在一個類 型中混用深拷貝和淺拷貝會導致很多不一致的問題。一但你涉及到ICloneable這 個問題,這樣的混用就很難解脫了。大多數時候,我們應該完全避免使用 ICloneable,讓類更簡單一些。這樣使用和實現都相對簡單得多。

任何 只以內置類型做為成員的值類型不必支持ICloneable; 用簡單的賦值語句對結構 的所有值進行拷貝比Clone()要高效得多。Clone()方法必須對返回類型進行裝箱 ,這樣才能強制轉化成一個System.Object的引用。而調用者還得再用強制轉化 從箱子中取回這個值。我知道你已經有足夠的能力這樣做,但不要用Clone()函 數來取代賦值語句。

那麼,當一個值類型中包含一個引用類型時又會怎 樣呢?最常見的一種情況就是值類型中包含一個字符串:

public struct ErrorMessage
{
 private int errCode;
 private int details;
 private string msg;
// details elided
}

字符串是一個特殊情況,因為它是一個恆定類。如果你指定了一 個錯誤消息串,那麼所有的錯誤消息類都引用到同一個字符串上。而這並不會導 致任何問題,這與其它一般的引用類型是不一樣的。如果你在任何一個引用上修 改了msg變量,你會就為它重新創建了一個string對象(參見原則7)。

(譯 注:string確實是一個很有意思的類,很多C++程序員對這個類不理解,也很有 一些C#程序對它不理解,導致很多的低效,甚至錯誤問題。應該好好的理解一下 C#裡的string(以及String和StringBulider之間的關系)這個類,這對於學好C# 是很有幫助的。因為這種設計思想可以沿用到我們自己的類型中。)

一般 情況,如果一個結構中包含了一個任意的引用類型,那麼拷貝時的情況就復雜多 了。這也是很少見的,內置的賦值語句會對結構進行淺拷貝,這樣兩個結構中的 引用變量就引用到同個一個對象上。如果要進行深拷貝,那麼你就必須對引用類 型也進行拷貝,而且還要知道該引用類型上是否也支持用Clone()進行深拷貝。 不管是哪種情況,你都不用對值類型添加對ICloneable的支持,賦值語句會對值 類型創建一個新的拷貝。

一句概括值類型:沒有任何理由要給一個值類 型添加對ICloneable接口的支持! 好了,現在讓我們再看看引用類型。引用類型 應該支持ICloneable接口,以便明確的給出它是支持深拷貝還是淺拷貝。明智的 選擇是添加對ICloneable的支持,因為這樣就明確的要求所有派生類也必須支持 ICloneable。看下面這個簡單的繼承關系:

class BaseType : ICloneable
{
 private string _label = "class name";
 private int [] _values = new int [ 10 ];
  public object Clone()
 {
  BaseType rVal = new BaseType( );
  rVal._label = _label;
  for( int i = 0; i < _values.Length; i++ )
   rVal._values[ i ] = _values[ i ];
  return rVal;
 }
}
class Derived : BaseType
{
 private double [] _dValues = new double[ 10 ];
  static void Main( string[] args )
 {
  Derived d = new Derived();
  Derived d2 = d.Clone() as Derived;
  if ( d2 == null )
   Console.WriteLine( "null" );
  }
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved