Effective C# Item 8 : Ensure That 0 Is a Valid State for Value Types
.Net中預設的初始化是將對象的所有資訊都置0。對於實值型別來說,我們不能避免在建立新的實值型別執行個體的時候將其值置為0。這是類型的預設值。
唯一一個特殊情況是枚舉(enum)。我們不應該建立一個不包含0作為其值的枚舉。所有的枚舉都繼承自System.ValueType。枚舉值是從0開始的,但是我們可以修改它:(P.S. 雖然現在Pluto已經不是太陽系的一員了,bless一下)
public enum Planet
{
Mercury = 1,
Venus = 2,
Earth = 3,
Mars = 4,
Jupiter = 5,
Saturn = 6,
Neptune = 7,
Uranus = 8,
Pluto = 9
}
Planet sphere = new Planet();
現在sphere的值就是0,這不是一個合法的值。雖然不會報錯,但是0並不代表任何含義。當我們建立自己的引用時,應當確保0是其中的一個枚舉值。如果你將枚舉聲明為二進位類型的,需要定義0來代表None。
如果這樣發展下去的話,我們只能強迫使用者在初始化的時候就為其明確賦值。
Planet sphere = Planet.Mars;
這將使得我們很難在其他的類型中使用這種類型,因為我們並不知道要將其初始化為何值。
使用這樣的類型建立對象將會建立一個不合法的Planet。
ObservationData d = new ObservationData();
我們必須確保0是一個有效值。如果可以的話應當將其對應為為最佳預設值。對於Planet枚舉來說,沒有什麼最佳可言。當使用者沒有使用的時候,它應當不包含任何含義。我們可以使用0來改造我們的枚舉使其更加合理:
public enum Planet
{
None = 0,
Mercury = 1,
Venus = 2,
Earth = 3,
Mars = 4,
Jupiter = 5,
Saturn = 6,
Neptune = 7,
Uranus = 8,
Pluto = 9
}
Planet sphere = new Planet();
sphere現在對應的枚舉值是None,代表還沒有使用者資料。在這裡添加預設值對ObservationData造成的影響就是新初始化的對象的枚舉值是0,代表的含義是None。之後可以在ObservationData的建構函式中對其進行修改。要注意的是預設的建構函式仍然有效。使用者可以構造一個預設值的對象,我們不能阻止他們這樣做。
在我們結束enum的討論之前,我們還有必要說一下在使用flag的時候枚舉的一些特殊情況。使用Flags屬性時我們應當將代表None含義的枚舉值設為0:(P,S.關於Flags枚舉的情況請看我寫的另一篇介紹:FlagsAttribute屬性在enum中的應用)
[Flags]
public enum Styles
{
None = 0,
Flat = 1,
Sunken = 2,
Raised = 4
}
許多開發人員通過二進位操作符&來使用flags枚舉。不合理的0值會造成嚴重的問題,如果flag為0值則下例中的條件會永遠不能成立:
if((flag & Styles.Flat) != 0)
DoSth(); //如果flag為0則永遠不會執行
因此如果使用Flags,我們應當確定0代表的含義是None。
另外的常見初始化問題發生在實值型別中包含的參考型別上。String就是一個常見的例子(P.S. .Net中string是參考型別,雖然某些操作符的重載會給使用者帶來實值型別的錯覺)。
public struct LogMessage
{
private int _ErrLevel;
private string _msg;
}
LogMessage MyMessage = new LogMessage();
MyMessage包含了一個為null的參考型別。這樣並不會引起任何問題。但是當你使用屬性來擷取它的值的時候就可能會出現問題。因此我們要為其添加如果為null則返回string.Empty的邏輯。在我們的結構中我們應當這樣檢驗屬性是否為null。
public struct LogMessage
{
private int _ErrLevel;
private string _msg;
public string Message
{
get
{
return(_msg != null)? _msg:string.Empty;
}
set
{
_msg = value;
}
}
}
系統初始化時所有的類型的值都為0,這是不能避免的。可能的話應當將0設為最初的預設值。特殊情況下,例如flags枚舉,應當讓其代表None。
譯自 Effective C#:50 Specific Ways to Improve Your C# Bill Wagner著
回到目錄