[C#]Effective C# 條款八: 確保0為值類型的有效狀態


.NET程式在物件初始時,變數初始器會將成員變數做初始化的動作。對於值類型的成員變數來說,會被初始為0值。因此我們應將0視為值類型的默認值。



以列舉型別來看,假設今天我有一個列舉型別:



enum Sex
{
Boy=1,
Girl=2
}









其列舉值並未從0,而是從1開始。則初始值0對該列舉來說,是一個無效的狀態。程式也可能因此無法正常運作。像是:




class Program
{
static void Main(string[] args)
{
Person p=new Person();
//p.Sex = Sex.Boy;
Console.WriteLine(p.Sex.ToString ());
}
}
enum Sex
{
Boy = 1,
Girl = 2
}
struct Person
{
public String Name;
public Sex Sex ;
}









運行結果如下:



image





但在正常的情況下,列舉值應該要在列舉範圍內,運行結果應顯示如下這般:



image





這樣的問題我們無法透過建構子給予初始值來解決,因為就算指定了具備參數的建構子。




struct Person
{
public String Name;
public Sex Sex ;
public Person(String name,Sex sex)
{
this.Name = name;
this.Sex = sex;
}
}

















對於值類型來說仍有預設建構子可以使用。像是:




class Program
{
static void Main(string[] args)
{
Person p=new Person();
Console.WriteLine(p.Sex.ToString ());
}
}
enum Sex
{
Boy = 1,
Girl = 2
}

struct Person
{
public String Name;
public Sex Sex;

public Person(String name, Sex sex)
{
this.Name = name;
this.Sex = sex;
}
}







且值類型的預設建構子無法自行撰寫,若嘗試撰寫編譯器會發出”Structs cannot contain explicit parameterless constructors”的錯誤。



image





而若想透過變數初始器來做設定的動作,編譯器也會以”cannot have instance field initializers”錯誤告知。



image











因此我們在使用值類型時特別留意其初始值,確保0為值類型的有效狀態,避免上述問題的發生。就像這樣:




class Program
{
static void Main(string[] args)
{
Person p=new Person();
Console.WriteLine(p.Sex.ToString ());
}
}
enum Sex
{
NotSet = 0,
Boy = 1,
Girl = 2
}

struct Person
{
public String Name;
public Sex Sex;

public Person(String name, Sex sex)
{
this.Name = name;
this.Sex = sex;
}
}







除此之外,當撰寫設有Flags屬性的列舉型別,我們也要特別留意。除必須確保初始值0為有效狀態外,還需讓其值為沒有設定任何標記的狀態。像是:




[Flags]
public enum Styles
{
None=0,
Flat=1.
Sunken=2,
Raised=4
}









這是因為設有Flags屬性的列舉型態,我們可能會拿來做像下面這樣的運算:




if((flag & Styles.Flat) != 0)
DoFlatThings();







當Flat是0的話,判斷式依舊為false,達不到預期的效果。不過,這例子只是告知建議遵守該條款的原因。事實上,這樣的問題可以像下面這樣避開:




if((flag & Styles.Flat) == Styles.Flat)
DoFlatThings();