《Effective C#》Item 8:確保“0”在值類型中是有效的

大家在看了這標題後,肯定會覺的,爲什麽要確保“0”是有效的,這一點主要是跟值類型的內存分配有關,值類型有默認的構造函數,這是沒法避免的,因此值類型變量中的成員在初始化的時候所出現的細微問題(對于值類型這方面的知識可以參看我前面一篇文章)。

http://blog.csdn.net/Knight94/archive/2006/07/01/861383.aspx

日常中比較常見的兩種值類型,一個是enum定義的枚舉類型,一個是用struct定義的數據類型。那我接下來就分別說明這兩種類型如果不定義“0”爲一個有效值的所能造成的後果,再分別說明如何加以修改來彌補這類錯誤。

對于enum定義的枚舉類型來說,默認的起始值爲0,但是如果在定義的時候,按照如下的方式去定義的話,有時候會造成意想不到的錯誤。

public enum ERROR_CODE

{

INVALID_FILE_NAME = 1001,

CANT_BE_ACCESS = 1002,

}

如上的定義是沒有默認值0的,那麽當我顯示的去定義一個此類型變量,例如:

ERROR_CODE myError = new ERROR_CODE();

Debug.WriteLine( string.Format( "{0}", (int)myError ) );

可以看出如上的輸出結果是0,但是它不是一個有效的值,也許這種錯誤可能很少出現,但是在struct複合類型中這種錯誤表現得更爲明顯。例如:

public struct ErrorMsg

{

private ERROR_CODE err;

private string strMsg;

// other properties or methods here

}

對于如上的結構類型來說,由于默認構造函數存在,所以當創建一個ErrorMsg變量的時候,那麽對于err這個成員的值是無效的。有人可能會說,增加構造函數來限制等等的做法來避免這類錯誤。那麽爲什麽與其在ErrorMsg中增加限制,不如在定義ERROR_CODE的時候就避免呢。其實這類錯誤的避免方法很簡單,只要在ERROR_CODE中增加0的有效定義即可,例如比較正確形式的應該如下:

public enum ERROR_CODE

{

NO_ERROR = 0,

INVALID_FILE_NAME = 1001,

CANT_BE_ACCESS = 1002,

}

按照如上的做法就可以避免定義ERROR_CODE變量或者被組合到別的類型中的時候,都不會發生初始值是無效的尴尬局面。

在這本書中,對于枚舉類型還提到一點,就是增加Flags標記的時候,一定要以None作爲0,例如:

[Flags]

public enum Style

{

None = 0,

Flat = 1,

}

這樣的目的是爲了在與和或操作的時候能有意義。這方面的例子我就不多說了,例如:某個connection的狀態,或者文件的某種屬性等等都會用到類似的做法。

除了枚舉類型外,對于struct定義的值類型來說,如果struct類型所包含的成員類型都是值類型的話,是沒有什麽問題(如果是自定義枚舉類型,要參照上面所說的方法進行處理);但是如果包含引用類型,則需要進行初始化,避免在訪問的時候出現異常。例如對于前面所說的ErrorMsg來說,由于struct默認構造函數的存在,有可能在ErrorMsg類型變量在構造後,其的strMsg成員不是有效的。除了string類型外,常見的還有數組類型。如何避免類似的問題呢,方法是采用屬性來實現。例如:

public struct ErrorMsg

{

private ERROR_CODE err;

private string strMsg;

public string Message

{

get

{

if( strMsg == null ) return string.Empty;

else

return strMsg;

}

}

// other properties or methods here

}

public struct IntArray

{

private int[] nArray;

public int[] Array

{

get

{

if( nArray == null )

nArray = new int[10];

return nArray;

}

}

}

所以在論壇上經常看到有人提出不能在struct中定義一個固定維數的數組,那麽參照如上的方法,這類問題就能得到很好地解決。

最後再提一句,對于要確保“0”在所定義的值類型中是有效的這一點來說別小看。屏蔽錯誤從細微做起,而且好的編碼習慣會使後面的工作事半功倍。

《Effective C#》Item 6:區分值類型和引用類型
在C#中有兩種類型的數據,一種是值類型數據,一種是引用類型數據。在編碼的時候區分這兩種類型數據,可以避免一些細小的編碼錯誤。 首先說說什麽類型是值類型,例如:int、float、bool之類的基礎類型,以及用struct定...查看完整版>>《Effective C#》Item 6:區分值類型和引用類型
 
《Effective C#》Item 7:推薦使用不可改變的原子值類型
首先來解釋一下標題,原標題爲《Prefer Immutable Atomic Value Type》,因此對于標題的理解要分成三部分,第一部分爲不可改變,第二部分爲原子,最後一個部分爲值類型。最後一部分,我不多說了,限制此章適用的範圍...查看完整版>>《Effective C#》Item 7:推薦使用不可改變的原子值類型
 
《Effective C#》Item 5:提供一個有意義的ToString函數
在編寫自定義類型的時候,即使我們不寫ToString函數,系統也會自動提供ToString函數,例如: public class clsUserInfo { private string strUserName; …… } 不過系統所提供To...查看完整版>>《Effective C#》Item 5:提供一個有意義的ToString函數
 
《Effective C#》Item 2:定義常量的兩種方法
在C#中定義常量的方式有兩種,一種叫做靜態常量(Compile-time constant),另一種叫做動態常量(Runtime constant)。前者用“const”來定義,後者用“readonly”來定義。 對于靜態常量(Compi...查看完整版>>《Effective C#》Item 2:定義常量的兩種方法
 
《Effective C#》Item 1:用屬性來訪問類的私有成員
在程序中,難免要訪問某個對象的私有成員。那麽以前實現這類功能的方法有兩種,第一種方法最簡單,就是把成員訪問符從“private”改爲“public”即可;而另一個就是提供公有的成員訪問函數來進行...查看完整版>>《Effective C#》Item 1:用屬性來訪問類的私有成員
 
《Effective C#》Item 17:減少裝箱(Boxing)和拆箱(Unboxing)操作
爲了便于文章的開展,首先介紹裝箱(Boxing)和拆箱(Unboxing)這兩個名詞。.Net的類型分爲兩種,一種是值類型,另一種是引用類型。這兩個類型的本質區別,值類型數據是分配在棧中,而引用類型數據分配在堆上。那麽...查看完整版>>《Effective C#》Item 17:減少裝箱(Boxing)和拆箱(Unboxing)操作
 
《Effective C#》Item 14:使用構造函數初始化語句
在寫程序的時候,往往會出現爲一個類型提供不同場景的構造函數,可能大多構造函數比較相似,而C#中又不允許缺省參數,因此構造函數的編寫無疑是一個重複工作。但是使用Copy和Paste來完成構造函數的編寫,有時候很難達...查看完整版>>《Effective C#》Item 14:使用構造函數初始化語句
 
《Effective C#》Item 13:使用靜態構造函數初始化靜態成員
編寫程序的時候難免要使用到靜態成員,由于靜態成員的訪問是脫離類型對象的,所以使用非靜態構造函數,私有方法或者一些其他方法都是不合理的。.Net提供了成員初始化語句和靜態構造函數來初始化靜態成員。 根據上一個...查看完整版>>《Effective C#》Item 13:使用靜態構造函數初始化靜態成員
 
《Effective C#》Item 12:推薦使用成員初始化語句
爲了方便內容的開展,我先說說一個對象的構造過程。對于類型第一個實例的構造過程大致如下:1. 分配靜態成員的內存空間,此時空間存儲數據爲0;2. 執行靜態成員的初始化語句;3. 執行基類的靜態構造函數;4. 執行...查看完整版>>《Effective C#》Item 12:推薦使用成員初始化語句
 
 
回到王朝網路首頁