C# 列舉

小提示

剛開始開發軟體嗎? 先從 入門 教學開始。 當你需要在程式碼中表示一組固定的選擇時,就會遇到列舉型別。

有其他語言的經驗嗎? C# 枚舉的運作方式類似 Java 或 C++ 中的枚舉,並額外支援位元旗標與模式匹配。 瀏覽 旗標switch 表達式 區段,以尋找 C# 特定的模式。

舉型別 (或列 )定義了一組以整數值為後盾的命名常數。 當值必須是固定選項之一時,例如星期幾、HTTP 狀態碼、日誌等級或方向,則使用枚舉。 枚舉讓你的程式碼更易讀,也比原始整數常數更不容易出錯,因為編譯器會強制執行指定的值。

宣告一個枚舉

使用 enum 關鍵字來定義列舉,其後緊跟著型別名稱及其成員:

enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
}

預設底層型別為 int,值從 開始 0 ,遞增一。 Season.Spring0Season.Summer1、 ,依此類推。

指定底層型別及明確值

你可以選擇不同的積分類型,並指定明確的數值來控制數值表示:

enum HttpStatus : ushort
{
    OK = 200,
    NotFound = 404,
    InternalServerError = 500
}

當數字具有外部意義,例如HTTP狀態碼或協定識別碼時,請使用明確的值。 底層型態可以是任何整數型別,唯獨char除外。 使用byteshortushortintuintlong或。ulong

在交換表達式中使用枚舉

枚舉自然地與 switch 表達式和模式匹配合作。 若未處理所有成員,編譯器會警告你,這有助於在之後新增值時避免錯誤。

static string DescribeSeason(Season season) => season switch
{
    Season.Spring => "Flowers bloom and temperatures rise.",
    Season.Summer => "Long days and warm weather.",
    Season.Autumn => "Leaves change color and fall.",
    Season.Winter => "Short days and cold temperatures.",
    _ => throw new ArgumentOutOfRangeException(nameof(season))
};
var today = Season.Autumn;
Console.WriteLine(DescribeSeason(today));

棄置模式(_)處理任何未明確列出的值。 由於列舉的底層型別是整數,變數可能包含不對應任何命名成員的值。 例如, (Season)99 在執行時有效。 丟棄模式確保交換運算式能安全地處理這些意外值。 模式匹配 是一種 C# 功能,用來測試一個數值與形狀或條件的關係。 在這個例子中,每個 case 單位檢查枚舉是否與特定成員相符。 switch 表達式是多種模式匹配形式之一。 欲了解更多模式匹配資訊,請參閱 模式匹配

位元標誌

當枚舉代表多個選擇的組合而非單一選擇時,將每個成員定義為二的冪次方,然後應用 FlagsAttribute

[Flags]
enum FileAccess
{
    None = 0,
    Read = 1,
    Write = 2,
    Execute = 4,
    ReadWrite = Read | Write,
    All = Read | Write | Execute
}

利用運算子合併數值 | ,並用以下方法 HasFlag測試個別旗標:

var permissions = FileAccess.Read | FileAccess.Write;

Console.WriteLine(permissions);                          // ReadWrite
Console.WriteLine(permissions.HasFlag(FileAccess.Read)); // True
Console.WriteLine(permissions.HasFlag(FileAccess.Execute)); // False

屬性 [Flags] 也會影響 ToString()。 它以逗號分隔的名稱(例如 Read, Write)顯示合併值,而非原始數字。 如需詳細資訊,請參閱System.FlagsAttribute

在枚舉與整數之間轉換

明確的型別轉換表達式 用於在列舉與其底層整數型別之間進行轉換。 顯式轉型使用(Type)value語法結構告訴編譯器您打算進行轉換:

var status = HttpStatus.NotFound;
ushort code = (ushort)status;
Console.WriteLine($"Status: {status} ({code})"); // Status: NotFound (404)

var fromCode = (HttpStatus)200;
Console.WriteLine(fromCode); // OK

請注意,將整數強制轉換為列舉類型並無法驗證該值是否符合定義的成員。 當你接受外部數字輸入時,請使用 Enum.IsDefined 檢查其有效性。

解析字串與迭代值

Enum基底類別提供解析字串及遍歷所有定義值的方法:

// Parse a string to an enum value:
var parsed = Enum.Parse<Season>("Winter");
Console.WriteLine(parsed); // Winter

// Try to parse safely. It returns false only when the input can't be parsed. Call Enum.IsDefined to validate named members:
if (Enum.TryParse<Season>("Monsoon", out var unknown))
{
    Console.WriteLine(unknown);
}
else
{
    Console.WriteLine("'Monsoon' is not a valid Season"); // 'Monsoon' is not a valid Season
}

// Iterate over all values in an enum:
foreach (var season in Enum.GetValues<Season>())
{
    Console.WriteLine($"{season} = {(int)season}");
}
// Spring = 0
// Summer = 1
// Autumn = 2
// Winter = 3

當輸入可能無效時,請使用 Enum.TryParse<TEnum>(String, Boolean, TEnum) 代替 Enum.Parse<TEnum>(String) 。 它會回來 false 而不是拋出例外。

另請參閱