這個做法在建議處理 Value Types 相關的型別時因為預設初始化為 0,所以要確保為 0 的時候型別要能正常運作或者是有效的狀態。
在 .net 中預設的初始化就是將物件設定為 0,所以只能夠把 0 當成你的型別中的有效預設值,像是在 DecompressionMethods
這個 enum 中
0 就代表 None
,也能發現它是從 0 開始編號的。
void Main()
{
Console.WriteLine(default(int));
Console.WriteLine(default(HttpStatusCode));
}
[Flags]
public enum DecompressionMethods
{
None = 0,
GZip = 1,
Deflate = 2,
Brotli = 4,
}
例如在這個例子中,sphere
與 anotherSphere
變數的值都是 0。
public enum Planet
{
Mercury = 1,
Venus = 2,
Earth = 3,
Mars = 4,
Jupiter = 5,
Saturn = 6,
Uranus = 7,
Neptune = 8
}
Planet sphere = new Planet();
var anotherSphere = default(Planet);
那麼使用 Planet
enum 的 ObservationData
物件,就會產生不正確的狀態,對於 double 來說 0 是一個很合理得值,
但是對於 whichPlanet
來說 0 是沒有意義的預設值,這會導致 ObservationData
物件處在一個不正確的狀態,
語意上來說一筆 0 的行星且星等為 0 的觀察資料也不通順。
public struct ObservationData
{
private Planet whichPlanet;
private double magnitude;
}
ObservationData d = new ObservationData();
如果在 Planet
enum 中新增預設值 None,這樣語意就變成一筆不存在行星且星等為 0 的觀察資料,這樣就明確了許多。
public enum Planet
{
None = 0,
Mercury = 1,
Venus = 2,
Earth = 3,
Mars = 4,
Jupiter = 5,
Saturn = 6,
Uranus = 7,
Neptune = 8
}
另外要注意使用 FlagsAttribute 的時候一定要設定 None = 0
,因為在進行 bitwise AND 運算的時候如果是 0 並且沒有設定 None
的場合,
會導致下面的 if 判斷永遠不會進入。
[Flags]
public enum Styles
{
None = 0,
Flat = 1,
Sunken = 2,
Raised = 4,
}
void Main()
{
Styles flag = Styles.Sunken;
if ((flag & Styles.Flat) != 0) // Never true if Flat == 0.
Console.WriteLine(1);
}
還有一個常見的初始化問題,那就是一個內部包含Reference Types 的 Value Types,在初始化的時候 mag 欄位會為 null。
void Main()
{
LogMessage MyMessage = new LogMessage();
}
public struct LogMessage
{
private int ErrLevel;
private string msg;
}
要處理這個問題可以建立一個屬性並添加邏輯將預設值設定為 string.Empty,這樣就可以避免 null 檢查散落到程式各處。
public struct LogMessage
{
private int ErrLevel;
private string msg;
public string Message
{
get => msg ?? string.Empty;
set => msg = value;
}
}
Summary
系統會預設把所有 Value Types 的實例化設定為 0,所以必須避免我們的程式碼在處理 0 值的問題時發生問題,所以要假設使用者傳入的參數 全部為 0 當成預設的情況,並要注意使用 FlagsAttribute 的時候一定要設定 0 用來代表沒有任何 flags 的意思。