列舉基礎語法
// 預設底層型別為 int,值從 0 遞增
enum Color { Red, Green, Blue }
// 指定底層型別 (byte, sbyte, short, ushort, int, uint, long, ulong)
enum ColorByte : byte { Red = 6, Green = 8, Blue = 10 }
// 明確指定數值 (可重複、可不連續)
enum Status { Unknown = -1, Failed = 0, Success = 1, Pending = 100 }
Flags 屬性:位元旗標列舉
[Flags]
enum FileAccess
{
None = 0,
Read = 1, // 2^0
Write = 2, // 2^1
Execute = 4, // 2^2
ReadWrite = Read | Write, // 3
All = Read | Write | Execute // 7
}
// 使用
FileAccess access = FileAccess.Read | FileAccess.Write; // 3
bool canRead = (access & FileAccess.Read) != 0; // true
bool canExec = access.HasFlag(FileAccess.Execute); // false (C# 4+)
// 列舉所有旗標
foreach (FileAccess flag in Enum.GetValues(typeof(FileAccess)))
{
if (access.HasFlag(flag) && flag != FileAccess.None)
Console.WriteLine(flag); // Read, Write
}
核心靜態方法 (System.Enum)
| 方法 | 說明 | 範例 |
| GetValues | 取得所有值陣列 | Enum.GetValues(typeof(Color)) |
| GetNames | 取得所有名稱字串陣列 | Enum.GetNames(typeof(Color)) |
| Parse | 字串轉列舉 (區分大小寫) | Enum.Parse("Red") |
| TryParse | 安全轉換,回傳 bool | Enum.TryParse("Red", out Color c) |
| IsDefined | 檢查值是否定義於列舉 | Enum.IsDefined(typeof(Color), 99) |
| GetName | 值轉名稱 | Enum.GetName(typeof(Color), 0) |
| HasFlag | Flags 列舉檢查旗標 | access.HasFlag(FileAccess.Read) |
型別轉換與底層值存取
Color c = Color.Red;
int underlying = (int)c; // 0
byte b = (byte)ColorByte.Red; // 6
// 取得底層型別
Type underlyingType = Enum.GetUnderlyingType(typeof(ColorByte)); // System.Byte
進階:模式比對 (C# 7+) 與 Switch Expression (C# 8+)
public string Describe(Color color) => color switch
{
Color.Red => "紅色:熱情",
Color.Green => "綠色:自然",
Color.Blue => "藍色:冷靜",
_ => "未知顏色"
};
// 結合屬性模式
public string DescribeStatus(Status s) => s switch
{
Status.Success => "成功",
Status.Failed => "失敗",
Status.Pending => "待處理",
_ when s < 0 => "錯誤狀態",
_ => $"未定義代碼:{(int)s}"
};
效能考量:避免 Boxing
// 不好:每次 ToString() 都會 Boxing
string s1 = color.ToString();
// 較好:使用 nameof 或預先快取
string s2 = nameof(Color.Red); // 編譯期常數 "Red"
// Flags 列舉格式化
[Flags] enum Perm { None=0, R=1, W=2, X=4 }
Perm p = Perm.R | Perm.W;
Console.WriteLine(p); // 輸出 "R, W" (自動解析 Flags)
Console.WriteLine(p.ToString("F")); // "R, W" 完整旗標名
Console.WriteLine(p.ToString("D")); // "3" 數值
Console.WriteLine(p.ToString("X")); // "00000003" 十六進位
最佳實踐清單
- 單一值列舉:預設
int 即可,值從 0 開始
- Flags 列舉:必須加
[Flags]、值用 2 的冪次、提供 None = 0
- 底層型別:預設
int;記憶體敏感或互通需求才用 byte/short
- 命名慣例:列舉型別單數 PascalCase (
Color);成員 PascalCase (Red);Flags 列舉型別複數 (FileAccess)
- 避免公開列舉變更:公開 API 的列舉一旦發布,增減成員會破壞二進位相容性
- 使用
Enum.IsDefined 驗證外部輸入,防止非法值
實用擴充方法
public static class EnumExtensions
{
public static IEnumerable GetFlags(this T value) where T : Enum
{
foreach (T flag in Enum.GetValues(value.GetType()))
if (value.HasFlag(flag) && !flag.Equals(default(T)))
yield return flag;
}
public static string ToDescription(this T value) where T : Enum
{
var field = value.GetType().GetField(value.ToString());
var attr = field?.GetCustomAttribute();
return attr?.Description ?? value.ToString();
}
}
// 使用
[Flags] enum Perm { None=0, R=1, W=2 }
Perm p = Perm.R | Perm.W;
foreach (var f in p.GetFlags()) Console.WriteLine(f); // R, W