C# enumとは?定義・使い方・int変換・文字列変換まで初心者向けに徹底解説
はじめに
C#でステータスや種類を表現するとき、数値や文字列をそのまま使用すると、コードの意味が分かりにくくなります。
たとえば、注文状態を次のような数値で管理しているケースを考えてみましょう。
int orderStatus = 2;このコードだけを見ても、「2」が何を意味しているのか判断できません。コメントや仕様書を確認しなければ、処理中なのか発送済みなのか分からないでしょう。
C#のenumを使うと、次のように意味のある名前で状態を表現できます。
OrderStatus orderStatus = OrderStatus.Shipped;Shippedという名前から、注文が発送済みであることをすぐに理解できます。
enumはコードの可読性を高めるだけでなく、入力ミスを減らし、メソッドが受け取れる値を明確にする効果もあります。
1. C#のenum(列挙型)とは
1-1. enumは複数の定数を名前で管理する仕組み
enumは「enumeration」の略で、日本語では列挙型と呼ばれます。
関連する複数の整数定数を、意味のある名前でまとめて管理するための値型です。
たとえば、注文状態をenumで表す場合は次のように定義します。
public enum OrderStatus{Pending,Processing,Shipped,Completed,Canceled}それぞれのメンバーには内部的に整数値が割り当てられています。
Console.WriteLine((int)OrderStatus.Pending); // 0Console.WriteLine((int)OrderStatus.Processing); // 1Console.WriteLine((int)OrderStatus.Shipped); // 2ただし、通常の処理では数値ではなく、OrderStatus.Shippedのような名前を使います。
1-2. enumを使うメリット
enumを使う主なメリットは次のとおりです。
コードの意味が分かりやすくなる
if (status == 2){// 2が何を意味するのか分かりにくい}enumを使うと、条件の意味が明確になります。
if (status == OrderStatus.Shipped){// 発送済みであることが分かる}使用できる値を限定できる
メソッドの引数をintにすると、どの数値を渡すべきか分かりません。
void UpdateStatus(int status){}enum型を指定すれば、使用可能な値が明確になります。
void UpdateStatus(OrderStatus status){}IDEの入力補完にもenumのメンバーが表示されるため、入力ミスを減らせます。
不正な文字列の混入を防ぎやすい
文字列で状態を管理すると、スペルミスや表記揺れが発生する可能性があります。
string status1 = "Shipped";string status2 = "shiped";string status3 = "SHIPPED";enumならコンパイル時にメンバー名が検証されます。
OrderStatus status = OrderStatus.Shipped;1-3. constやstatic readonlyとの違い
const、static readonly、enumはいずれも固定的な値を扱えますが、用途が異なります。
constは、コンパイル時に確定する単一の定数を定義するときに使用します。
public const int MaxRetryCount = 3;static readonlyは、実行時に一度だけ設定する値に適しています。
public static readonly DateTime ApplicationStartTime = DateTime.Now;enumは、関連する選択肢を一つの型としてまとめる場合に適しています。
public enum LogLevel{Debug,Information,Warning,Error}enumでは、LogLevelという独立した型が作られます。単なる整数定数の集まりよりも型安全性が高く、引数や戻り値にも使用しやすい点が特徴です。
1-4. enumが適しているケース・適していないケース
enumは、選択肢が少なく、内容が比較的固定されているケースに適しています。
具体的には、次のような値です。
注文状態
曜日
ユーザー区分
ログレベル
支払い方法
ファイル形式
処理結果
一方、次のようなケースには必ずしも適していません。
データベースから頻繁に追加・変更される分類
ユーザーが自由に登録できるカテゴリ
メンバーごとに複雑な処理や多数の情報を持たせたい場合
実行時に選択肢が変化する場合
種類が頻繁に増える場合は、クラスやデータベースのマスターテーブルを使う設計も検討しましょう。
2. C#でenumを定義する方法
2-1. enumの基本構文
enumは、enumキーワードを使って定義します。
アクセス修飾子 enum 列挙型名{メンバー1,メンバー2,メンバー3}実際の定義例は次のとおりです。
public enum Season{Spring,Summer,Autumn,Winter}C#では、型名やenumメンバー名にパスカルケースを使用するのが一般的です。
public enum PaymentMethod{CreditCard,BankTransfer,CashOnDelivery}2-2. enumのメンバーに数値を指定する方法
enumの各メンバーには、整数値を明示的に指定できます。
public enum HttpStatus{Ok = 200,BadRequest = 400,NotFound = 404,InternalServerError = 500}値を確認するには、整数型へキャストします。
int value = (int)HttpStatus.NotFound;Console.WriteLine(value); // 404
外部システムやデータベースで決められたコードと対応させる場合は、数値を明示的に指定するとよいでしょう。
2-3. 数値を省略した場合の初期値と連番のルール
数値を省略した場合、最初のメンバーには0が割り当てられ、その後は1ずつ増加します。
public enum Priority{Low, // 0Medium, // 1High // 2}途中のメンバーだけ値を指定すると、次のメンバーは指定した値から1ずつ増加します。
public enum Sample{A, // 0B = 10, // 10C, // 11D // 12}連番に見えても、将来的なメンバー追加によって値が変わる可能性があります。データベースや外部APIに保存する数値は、明示的に指定するほうが安全です。
public enum OrderStatus{Unknown = 0,Pending = 1,Processing = 2,Shipped = 3,Completed = 4,Canceled = 5}2-4. enumの基になる整数型を指定する方法
enumの基になる型は、デフォルトではintです。
次の整数型を指定できます。
bytesbyteshortushortintuintlongulong
基になる型は、enum名の後ろにコロンを付けて指定します。
public enum SmallStatus : byte{None = 0,Active = 1,Disabled = 2}大きな値を使用する場合は、longやulongを指定できます。
public enum LargeValue : long{First = 1L,Second = 10_000_000_000L}通常はデフォルトのintで問題ありません。データサイズや外部仕様に明確な理由がある場合に、別の整数型を選択しましょう。
2-5. クラス内・名前空間内でenumを定義する際の違い
enumは名前空間内に直接定義できます。
namespace SampleApp;public enum OrderStatus{Pending,Shipped,Completed}
public class Order{public OrderStatus Status { get; set; }}
この場合は、同じ名前空間内の複数のクラスから利用しやすくなります。
クラスの内部に定義することも可能です。
public class Order{public enum StatusType{Pending,Shipped,Completed}public StatusType Status { get; set; }
}
クラス内のenumを外部から使用する場合は、クラス名を付けます。
Order.StatusType status = Order.StatusType.Shipped;特定のクラスだけで使用するenumならクラス内、複数のクラスから利用するenumなら名前空間内に定義する方法が分かりやすいでしょう。
3. C#でenumを使う基本操作
3-1. enum型の変数を宣言・代入する方法
enum型の変数は、通常の型と同じように宣言できます。
OrderStatus status;値を代入する場合は、型名とメンバー名を指定します。
status = OrderStatus.Processing;宣言と代入を同時に行うこともできます。
OrderStatus status = OrderStatus.Shipped;プロパティの型としても使用できます。
public class Order{public int Id { get; set; }public OrderStatus Status { get; set; }
}
3-2. if文でenumの値を判定する方法
enumの値は、==演算子で比較できます。
OrderStatus status = OrderStatus.Shipped;if (status == OrderStatus.Shipped){Console.WriteLine("商品は発送済みです。");}
否定条件には!=を使用します。
if (status != OrderStatus.Canceled){Console.WriteLine("キャンセルされていない注文です。");}複数の値を判定する場合は、論理演算子を組み合わせます。
if (status == OrderStatus.Pending ||status == OrderStatus.Processing){Console.WriteLine("注文を処理しています。");}3-3. switch文・switch式でenumを分岐する方法
複数のenumメンバーごとに処理を分ける場合は、switch文が便利です。
switch (status){case OrderStatus.Pending:Console.WriteLine("受付待ちです。");break;case OrderStatus.Processing:Console.WriteLine("処理中です。");break;case OrderStatus.Shipped:Console.WriteLine("発送済みです。");break;case OrderStatus.Completed:Console.WriteLine("完了しています。");break;case OrderStatus.Canceled:Console.WriteLine("キャンセルされています。");break;default:Console.WriteLine("不明な状態です。");break;
}
値に応じて結果を返すだけなら、switch式を使うと簡潔に書けます。
string message = status switch{OrderStatus.Pending => "受付待ちです。",OrderStatus.Processing => "処理中です。",OrderStatus.Shipped => "発送済みです。",OrderStatus.Completed => "完了しています。",OrderStatus.Canceled => "キャンセルされています。",_ => "不明な状態です。"};_は、どのパターンにも一致しなかった場合に使用されます。未定義の数値がenumへ変換される可能性もあるため、外部入力を扱う場合は既定処理を用意すると安全です。
3-4. メソッドの引数・戻り値にenumを使う方法
enumはメソッドの引数に使用できます。
public static void UpdateOrderStatus(OrderStatus status){Console.WriteLine($"注文状態を{status}に変更します。");}呼び出す側は、使用する値を明確に指定できます。
UpdateOrderStatus(OrderStatus.Shipped);戻り値としても使用できます。
public static OrderStatus GetInitialStatus(){return OrderStatus.Pending;}メソッドの入出力をenum型にすると、どの種類の値を扱う処理なのかが明確になります。
3-5. enumの値を比較する方法
同じenum型の値は、==や!=で比較できます。
OrderStatus first = OrderStatus.Shipped;OrderStatus second = OrderStatus.Shipped;bool isSame = first == second;
Console.WriteLine(isSame); // True
大小比較が必要な場合は、基になる整数値へ変換できます。
bool isLater =(int)OrderStatus.Completed > (int)OrderStatus.Processing;ただし、enumの数値が必ずしも処理順や優先順位を表すとは限りません。順序として比較する設計なら、その意図が分かるように数値を明示的に割り当てましょう。
4. enumとintを相互変換する方法
4-1. enumをintに変換する方法
enumをintに変換するには、キャストを使用します。
OrderStatus status = OrderStatus.Shipped;int value = (int)status;
Console.WriteLine(value);
enumの基になる型がbyteの場合は、byteにキャストできます。
public enum UserType : byte{Guest = 0,Member = 1,Administrator = 2}UserType userType = UserType.Administrator;byte value = (byte)userType;
Convert.ToInt32を使用する方法もあります。
int value = Convert.ToInt32(status);基になる型がlongで、値がintの範囲を超える可能性がある場合は、Convert.ToInt64などを使いましょう。
4-2. intをenumにキャストする方法
intをenumに変換する場合もキャストを使用できます。
int value = 2;OrderStatus status = (OrderStatus)value;
Console.WriteLine(status);
ただし、C#ではenumに定義されていない数値もキャストできます。
int value = 999;OrderStatus status = (OrderStatus)value;
Console.WriteLine(status); // 999
キャストが成功したからといって、有効なenumメンバーとは限らない点に注意してください。
4-3. Enum.ToObjectで数値をenumに変換する方法
Enum.ToObjectを使って数値からenumを作成することもできます。
int value = 2;OrderStatus status =(OrderStatus)Enum.ToObject(typeof(OrderStatus), value);
ジェネリック型が実行時まで分からない処理や、リフレクションを利用する処理で使われることがあります。
通常のコードで型が分かっている場合は、単純なキャストのほうが簡潔です。
OrderStatus status = (OrderStatus)value;Enum.ToObjectでも未定義の数値を変換できるため、必要に応じて事前検証を行いましょう。
4-4. Enum.IsDefinedで有効な値か確認する方法
数値がenumに定義されているか確認するには、Enum.IsDefinedを使用します。
int value = 2;if (Enum.IsDefined(typeof(OrderStatus), value)){OrderStatus status = (OrderStatus)value;Console.WriteLine(status);}else{Console.WriteLine("定義されていない値です。");}
ジェネリック形式を利用できる環境では、次のようにも記述できます。
if (Enum.IsDefined((OrderStatus)value)){OrderStatus status = (OrderStatus)value;}文字列のメンバー名を確認することもできます。
bool isDefined =Enum.IsDefined(typeof(OrderStatus), "Shipped");Enum.IsDefinedは、大文字と小文字を区別します。
4-5. 未定義の数値をenumに変換する際の注意点
enumへの数値キャストでは、定義されていない値でも例外が発生しません。
OrderStatus status = (OrderStatus)100;この仕様により、データベースやAPIから取得した不正な値が、そのままアプリケーション内部に入り込む可能性があります。
外部から受け取った数値は、変換前に検証しましょう。
public static bool TryConvertOrderStatus(int value,out OrderStatus status){if (Enum.IsDefined(typeof(OrderStatus), value)){status = (OrderStatus)value;return true;}status = OrderStatus.Unknown;return false;
}
なお、Flags属性を付けたenumでは、複数フラグの組み合わせが個別メンバーとして定義されていないことがあります。その場合、正しい組み合わせでもEnum.IsDefinedがfalseになることがあるため、別の検証方法が必要です。
5. enumと文字列を相互変換する方法
5-1. enumを文字列に変換する方法
enumを文字列に変換する最も基本的な方法は、ToStringです。
OrderStatus status = OrderStatus.Shipped;string text = status.ToString();
Console.WriteLine(text); // Shipped
文字列補間でも、自動的に文字列表現へ変換されます。
Console.WriteLine($"現在の状態は{status}です。");Enum.GetNameを使う方法もあります。
string? name =Enum.GetName(typeof(OrderStatus), status);値に対応する名前が存在しない場合、Enum.GetNameはnullを返します。
5-2. ToStringで名前や数値形式を指定する方法
enumのToStringでは、書式指定文字列を指定できます。
OrderStatus status = OrderStatus.Shipped;Console.WriteLine(status.ToString("G"));Console.WriteLine(status.ToString("D"));Console.WriteLine(status.ToString("X"));
主な書式は次のとおりです。
G:一般形式。定義済みなら名前、未定義なら数値D:10進数形式X:16進数形式F:Flags形式
例として、Shippedの値が3の場合は次のようになります。
Console.WriteLine(status.ToString("G")); // ShippedConsole.WriteLine(status.ToString("D")); // 3Console.WriteLine(status.ToString("X")); // 00000003通常は引数なしのToString()で十分です。
5-3. Enum.Parseで文字列をenumに変換する方法
文字列をenumへ変換するには、Enum.Parseを使用できます。
string input = "Shipped";OrderStatus status =Enum.Parse<OrderStatus>(input);
型をtypeofで指定する形式もあります。
OrderStatus status =(OrderStatus)Enum.Parse(typeof(OrderStatus),input);ただし、変換できない文字列を指定すると例外が発生します。
OrderStatus status =Enum.Parse<OrderStatus>("InvalidStatus");外部入力やユーザー入力には、例外を発生させないEnum.TryParseを使うのが基本です。
5-4. Enum.TryParseで安全に文字列をenumに変換する方法
Enum.TryParseは、変換に成功した場合にtrue、失敗した場合にfalseを返します。
string input = "Shipped";if (Enum.TryParse<OrderStatus>(input, out OrderStatus status)){Console.WriteLine($"変換成功: {status}");}else{Console.WriteLine("変換できませんでした。");}
例外処理を書かずに安全な変換ができます。
ただし、TryParseが成功しても、定義済みメンバーとは限らない場合があります。数字の文字列を渡すと、未定義の数値でも変換に成功することがあるためです。
厳密に検証する場合は、Enum.IsDefinedも組み合わせます。
if (Enum.TryParse<OrderStatus>(input, out OrderStatus status) &&Enum.IsDefined(typeof(OrderStatus), status)){Console.WriteLine($"有効な値です: {status}");}else{Console.WriteLine("有効な注文状態ではありません。");}5-5. 大文字・小文字を区別せずに変換する方法
Enum.ParseやEnum.TryParseは、デフォルトでは大文字と小文字を区別します。
次の入力は、通常の設定ではShippedと一致しません。
string input = "shipped";大文字と小文字を区別しない場合は、ignoreCaseにtrueを指定します。
OrderStatus status =Enum.Parse<OrderStatus>(input,ignoreCase: true);TryParseの場合は次のとおりです。
if (Enum.TryParse<OrderStatus>(input,ignoreCase: true,out OrderStatus status)){Console.WriteLine(status);}APIのクエリ文字列や設定ファイルなど、入力時の大文字・小文字を統一できない場合に便利です。
5-6. 数字の文字列を変換する際の注意点
Enum.TryParseには、数字の文字列も渡せます。
string input = "2";bool success =Enum.TryParse<OrderStatus>(input,out OrderStatus status);
2に対応するメンバーが存在すれば、そのenum値になります。
しかし、未定義の数値でも変換に成功する可能性があります。
string input = "999";bool success =Enum.TryParse<OrderStatus>(input,out OrderStatus status);
Console.WriteLine(success); // Trueになる場合があるConsole.WriteLine(status); // 999
数字の文字列を許可しない場合は、先に数値として解釈できるか確認します。
string input = "999";if (int.TryParse(input, out _)){Console.WriteLine("数字の入力は許可されていません。");}else if (Enum.TryParse<OrderStatus>(input,ignoreCase: true,out OrderStatus status) &&Enum.IsDefined(typeof(OrderStatus), status)){Console.WriteLine(status);}
6. enumの値や名前を一覧で取得する方法
6-1. Enum.GetValuesで全メンバーを取得する方法
Enum.GetValuesを使用すると、enumに定義された値をすべて取得できます。
OrderStatus[] values =Enum.GetValues<OrderStatus>();取得した値は配列として扱えます。
foreach (OrderStatus value in values){Console.WriteLine(value);}従来の形式では、型を引数に指定します。
Array values =Enum.GetValues(typeof(OrderStatus));ジェネリック形式のほうが型変換を減らせるため、利用できる場合はEnum.GetValues<TEnum>()が便利です。
6-2. Enum.GetNamesで名前の一覧を取得する方法
enumメンバーの名前だけを取得するには、Enum.GetNamesを使用します。
string[] names =Enum.GetNames<OrderStatus>();一覧を表示する例は次のとおりです。
foreach (string name in names){Console.WriteLine(name);}型を指定する形式も利用できます。
string[] names =Enum.GetNames(typeof(OrderStatus));フォームや画面の選択肢を動的に作る場合に利用できます。ただし、メンバー名をそのまま日本語表示に使うのではなく、表示用属性や変換処理を用意する設計が一般的です。
6-3. foreachでenumの全要素を処理する方法
Enum.GetValuesとforeachを組み合わせると、全メンバーに対して処理できます。
foreach (OrderStatus status inEnum.GetValues<OrderStatus>()){int value = (int)status;Console.WriteLine($"名前: {status}, 値: {value}");
}
出力例は次のようになります。
名前: Unknown, 値: 0名前: Pending, 値: 1名前: Processing, 値: 2名前: Shipped, 値: 3選択肢の作成やログ出力、テストデータの生成などに活用できます。
6-4. 任意の値からenum名を取得する方法
数値からenumメンバー名を取得するには、Enum.GetNameを使用します。
int value = 3;string? name =Enum.GetName(typeof(OrderStatus),value);
Console.WriteLine(name);
値に対応するメンバーが存在しない場合はnullになります。
int value = 999;string? name =Enum.GetName(typeof(OrderStatus),value);
if (name is null){Console.WriteLine("対応する名前がありません。");}
キャストとToStringを組み合わせる方法もあります。
string text = ((OrderStatus)value).ToString();ただし、未定義の値の場合は数字の文字列が返されます。定義の有無を明確に判定したい場合は、Enum.GetNameまたはEnum.IsDefinedを使用しましょう。
6-5. 名前から対応する数値を取得する方法
名前から数値を取得するには、文字列をenumへ変換してから整数へキャストします。
string name = "Shipped";if (Enum.TryParse<OrderStatus>(name,out OrderStatus status)){int value = (int)status;
Console.WriteLine(value);
}
大文字と小文字を区別しない場合は、次のように記述します。
if (Enum.TryParse<OrderStatus>(name,ignoreCase: true,out OrderStatus status) &&Enum.IsDefined(typeof(OrderStatus), status)){int value = (int)status;}外部入力を扱う場合は、変換成功だけでなく、定義済みかどうかも確認すると安全です。
7. Flags属性で複数のenum値を組み合わせる方法
7-1. Flags属性とは
通常のenumは、複数の選択肢から一つの値を表現するために使用します。
一方、複数の値を同時に持たせたい場合は、Flags属性を使用します。
[Flags]public enum Permission{None = 0,Read = 1,Write = 2,Delete = 4}たとえば、「読み取り可能かつ書き込み可能」という状態を一つの変数で表現できます。
Permission permission =Permission.Read | Permission.Write;Flags属性は、ユーザー権限、機能設定、オプション指定などに適しています。
7-2. ビットフラグ用の値を定義する方法
Flags属性を使うenumでは、各メンバーに2の累乗を割り当てます。
[Flags]public enum Permission{None = 0,Read = 1, // 0001Write = 2, // 0010Delete = 4, // 0100Execute = 8 // 1000}ビットシフト演算子を使って定義することもできます。
[Flags]public enum Permission{None = 0,Read = 1 << 0,Write = 1 << 1,Delete = 1 << 2,Execute = 1 << 3}1 << 0は1、1 << 1は2、1 << 2は4を表します。
連番の0、1、2、3を割り当てると、ビットが重なって正しく組み合わせられません。
7-3. OR演算子で複数の値を設定する方法
複数のフラグを組み合わせるには、ビットOR演算子|を使用します。
Permission permission =Permission.Read |Permission.Write;後からフラグを追加する場合は、|=を使用できます。
permission |= Permission.Delete;この時点で、permissionには次の3つが含まれます。
ReadWriteDelete
Flags属性が付いているため、ToStringでは組み合わせた名前が表示されます。
Console.WriteLine(permission);// Read, Write, Delete7-4. HasFlagで特定の値を含むか判定する方法
特定のフラグが含まれているか確認するには、HasFlagを使用できます。
if (permission.HasFlag(Permission.Read)){Console.WriteLine("読み取り権限があります。");}複数のフラグがすべて含まれているか確認することもできます。
Permission required =Permission.Read |Permission.Write;if (permission.HasFlag(required)){Console.WriteLine("読み取りと書き込みの両方が可能です。");}
HasFlag(Permission.None)は、どの値に対しても真になり得るため、Noneの確認には使用しないほうが分かりやすいでしょう。
if (permission == Permission.None){Console.WriteLine("権限がありません。");}7-5. AND演算子でフラグを確認・解除する方法
ビットAND演算子&を使ってフラグを確認することもできます。
if ((permission & Permission.Read) ==Permission.Read){Console.WriteLine("読み取り可能です。");}フラグを解除するには、ビット否定演算子~とAND演算子を組み合わせます。
permission &= ~Permission.Write;この処理により、ほかのフラグを維持したままWriteだけを解除できます。
フラグの有無を反転する場合は、XOR演算子^を使用します。
permission ^= Permission.Delete;ただし、反転処理は現在の状態によって結果が変わるため、明示的に追加または解除するほうが意図を伝えやすい場合があります。
7-6. NoneやAllを定義する際のポイント
Flags属性を使うenumでは、通常、値が0のNoneを定義します。
None = 0すべての権限を表すAllを定義することもできます。
[Flags]public enum Permission{None = 0,Read = 1 << 0,Write = 1 << 1,Delete = 1 << 2,Execute = 1 << 3,All = Read | Write | Delete | Execute
}
All = -1と定義すると、将来使用する可能性があるビットまで立つことがあります。現在定義されているフラグだけを組み合わせるほうが、意図が明確です。
また、複数フラグをまとめた便利なグループを定義できます。
ReadWrite = Read | Write同じ組み合わせを頻繁に使用する場合に有効です。
8. enumに表示名や説明を持たせる方法
8-1. enumのメンバーに日本語名を付ける方法
C#では日本語の識別子も使用できます。
public enum OrderStatus{未処理,処理中,発送済み,完了}しかし、ソースコードの可読性や外部ツールとの連携を考えると、メンバー名には英語を使用し、表示時だけ日本語へ変換する方法が一般的です。
単純なケースなら、switch式で表示名を返せます。
public static string GetDisplayName(OrderStatus status){return status switch{OrderStatus.Unknown => "不明",OrderStatus.Pending => "受付待ち",OrderStatus.Processing => "処理中",OrderStatus.Shipped => "発送済み",OrderStatus.Completed => "完了",OrderStatus.Canceled => "キャンセル",_ => "不明"};}メンバーが多い場合や、複数の画面で利用する場合は属性を使用すると便利です。
8-2. Description属性から表示名を取得する方法
Description属性を使うと、enumメンバーに説明や表示名を設定できます。
using System.ComponentModel;public enum OrderStatus{[Description("不明")]Unknown = 0,
<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[Description("受付待ち")]</span>Pending = 1,<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[Description("処理中")]</span>Processing = 2,<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[Description("発送済み")]</span>Shipped = 3,<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[Description("完了")]</span>Completed = 4,<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[Description("キャンセル")]</span>Canceled = 5
}
属性の値は、リフレクションを使って取得します。
using System.ComponentModel;using System.Reflection;public static string GetDescription(OrderStatus status){FieldInfo? field =typeof(OrderStatus).GetField(status.ToString());
DescriptionAttribute? attribute =field?.GetCustomAttribute<DescriptionAttribute>();return attribute?.Description?? status.ToString();
}
使用例は次のとおりです。
string text =GetDescription(OrderStatus.Shipped);Console.WriteLine(text); // 発送済み
8-3. Display属性を利用する方法
ASP.NET Coreや画面表示に関係する処理では、Display属性を利用できます。
using System.ComponentModel.DataAnnotations;public enum OrderStatus{[Display(Name = "不明")]Unknown = 0,
<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[Display(Name = "受付待ち")]</span>Pending = 1,<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[Display(Name = "処理中")]</span>Processing = 2,<span data-placeholder-token="true" class="text-token-text-primary cursor-text rounded-sm" style="background-color: color-mix(in srgb, var(--theme-user-selection-bg, var(--selection)) 30%, transparent); padding-top: 4px; padding-bottom: 4px;">[Display(Name = "発送済み")]</span>Shipped = 3
}
表示名はリフレクションで取得できます。
using System.ComponentModel.DataAnnotations;using System.Reflection;public static string GetDisplayName(OrderStatus status){FieldInfo? field =typeof(OrderStatus).GetField(status.ToString());
DisplayAttribute? attribute =field?.GetCustomAttribute<DisplayAttribute>();return attribute?.GetName()?? status.ToString();
}
Display属性には、Name以外にも説明文や表示順などの情報を設定できます。
8-4. 拡張メソッドで表示名の取得処理を共通化する方法
さまざまなenumで同じ処理を使う場合は、拡張メソッドにすると便利です。
using System.ComponentModel;using System.Reflection;public static class EnumExtensions{public static string GetDescription(this Enum value){string name = value.ToString();
FieldInfo? field =value.GetType().GetField(name);DescriptionAttribute? attribute =field?.GetCustomAttribute<DescriptionAttribute>();return attribute?.Description ?? name;}
}
次のように呼び出せます。
OrderStatus status = OrderStatus.Shipped;string displayName =status.GetDescription();
Console.WriteLine(displayName);
リフレクション処理を大量に繰り返す場合は、結果をキャッシュする設計も検討しましょう。
8-5. enumとデータベース・JSONを連携する際の考え方
enumをデータベースへ保存する場合は、数値または文字列として保存します。
数値で保存するメリットは、データ量が小さく、メンバー名を変更しても保存済みデータに影響しにくいことです。
一方、データベースだけを見ても値の意味が分かりにくくなります。
文字列で保存すると意味を理解しやすい反面、メンバー名の変更が既存データとの互換性に影響します。
JSONでは、標準設定によってenumが数値として出力されることがあります。
{"status": 3}文字列として出力すれば読みやすくなります。
{"status": "Shipped"}外部システムと連携する場合は、次の点を事前に決めておきましょう。
数値と文字列のどちらで保存・送信するか
メンバー追加時の扱い
未知の値を受信した場合の処理
メンバー名を変更できるか
数値を将来変更しないか
9. C#のenumでよくあるエラーと注意点
9-1. 存在しない値でもキャストできてしまう
enumは、定義されていない数値でもキャストできます。
OrderStatus status =(OrderStatus)999;コンパイルエラーも実行時例外も発生しません。
外部データを変換する場合は、Enum.IsDefinedなどで確認しましょう。
int value = 999;if (!Enum.IsDefined(typeof(OrderStatus),value)){Console.WriteLine("不正な値です。");}
ただし、Flags属性付きenumでは、組み合わせた値が個別に定義されていないことがあります。Flagsの検証では、許可されていないビットが含まれていないかを確認する方法が適しています。
Permission allowed = Permission.All;Permission input = (Permission)5;bool isValid = (input & ~allowed) == 0;
9-2. enumの初期値が必ず0になる点に注意する
enum型のフィールドや配列要素は、初期化しなければ基になる数値が0の状態になります。
public class Order{public OrderStatus Status { get; set; }}Order order = new();Console.WriteLine((int)order.Status); // 0
これは、0に対応するメンバーを定義していなくても同じです。
public enum OrderStatus{Pending = 1,Shipped = 2}この場合、初期値は0ですが、どのメンバーにも対応しません。
9-3. 0に対応するメンバーを定義すべき理由
初期値を安全に扱うため、0に対応するメンバーを定義するのが一般的です。
public enum OrderStatus{Unknown = 0,Pending = 1,Shipped = 2}名前には、用途に応じて次のようなものを使用できます。
NoneUnknownUndefinedNotSet
0を有効な通常状態として扱うこともできますが、未設定状態と区別できなくなる可能性があります。
9-4. 値の重複を定義した場合の挙動
enumでは、複数のメンバーに同じ数値を指定できます。
public enum ResultCode{Success = 0,Ok = 0,Error = 1}これはコンパイルエラーになりません。
ただし、数値から名前へ変換したときに、どちらの名前が得られるかを前提にした処理は避けるべきです。
ResultCode result = (ResultCode)0;Console.WriteLine(result);
同じ値に複数の名前を持たせると、ログ出力、文字列変換、シリアライズで混乱しやすくなります。旧名称との互換性維持など、明確な理由がない限り重複は避けましょう。
9-5. enumのメンバー名を変更すると互換性に影響する
enumを数値として保存している場合、メンバー名の変更だけなら保存済みの数値には影響しません。
しかし、文字列として保存・送信している場合は、名前の変更が破壊的変更になります。
変更前が次の値だったとします。
OrderStatus.ShippedこれをDispatchedへ変更すると、既存のJSONや設定ファイルにある"Shipped"を読み込めなくなる可能性があります。
外部公開しているAPIでは、enumメンバー名も契約の一部として扱いましょう。
9-6. 値を追加した際にswitch文の修正が必要になる
enumへ新しいメンバーを追加した場合、そのenumを処理するswitch文も確認する必要があります。
public enum OrderStatus{Unknown,Pending,Processing,Shipped,Completed,Canceled,Returned}Returnedを追加しても、既存のswitch文が自動的に適切な処理を行うとは限りません。
string message = status switch{OrderStatus.Pending => "受付待ち",OrderStatus.Shipped => "発送済み",OrderStatus.Completed => "完了",_ => "その他"};既定処理によって不具合が隠れる場合もあります。メンバーを追加した際は、プロジェクト内のswitch文や条件分岐を検索して修正しましょう。
9-7. 状態や種類が頻繁に増える場合は別設計を検討する
enumは、選択肢が固定的な場合に適しています。
状態や種類が頻繁に追加され、それぞれが異なる処理、表示名、設定値、遷移ルールを持つ場合は、enumだけで管理するとswitch文が増えやすくなります。
そのような場合は、次の設計を検討できます。
クラスによる状態表現
Strategyパターン
Stateパターン
データベースのマスターテーブル
辞書による処理の割り当て
ポリモーフィズムを利用した設計
単純な分類ならenum、振る舞いを持つ複雑な概念ならクラスという考え方が一つの目安です。
10. enumの実践的な使用例
10-1. 曜日やステータスをenumで管理する例
営業時間の区分をenumで管理する例です。
public enum BusinessDayType{Unknown = 0,Weekday = 1,Saturday = 2,Holiday = 3}日付から区分を取得します。
public static BusinessDayType GetDayType(DateTime date){return date.DayOfWeek switch{DayOfWeek.Saturday=> BusinessDayType.Saturday, DayOfWeek.Sunday=> BusinessDayType.Holiday,_ => BusinessDayType.Weekday};
}
C#には曜日を表すDayOfWeekが標準で用意されています。既存のenumで要件を満たせる場合は、独自に定義せず標準型を活用しましょう。
10-2. 注文状態をswitch文で処理する例
注文状態に応じて、実行可能な処理を変更する例です。
public enum OrderStatus{Unknown = 0,Pending = 1,Processing = 2,Shipped = 3,Completed = 4,Canceled = 5}public static string GetAvailableAction(OrderStatus status){return status switch{OrderStatus.Pending=> "注文を確定できます。", OrderStatus.Processing=> "発送処理を実行できます。",OrderStatus.Shipped=> "配送状況を確認できます。",OrderStatus.Completed=> "処理は完了しています。",OrderStatus.Canceled=> "注文はキャンセルされています。",_ => "注文状態を確認できません。"};
}
使用例は次のとおりです。
OrderStatus status =OrderStatus.Processing;string action =GetAvailableAction(status);
Console.WriteLine(action);
10-3. 文字列入力をTryParseで安全に変換する例
ユーザーが入力した注文状態を変換する例です。
public static bool TryGetOrderStatus(string? input,out OrderStatus status){status = OrderStatus.Unknown;if (string.IsNullOrWhiteSpace(input)){return false;}if (int.TryParse(input, out _)){return false;}return Enum.TryParse(input,ignoreCase: true,out status)&&Enum.IsDefined(typeof(OrderStatus),status);
}
使用例は次のとおりです。
string input = "shipped";if (TryGetOrderStatus(input,out OrderStatus status)){Console.WriteLine($"変換結果: {status}");}else{Console.WriteLine("有効な注文状態ではありません。");}
数字の文字列を受け付ける場合は、int.TryParseで変換した後、Enum.IsDefinedで検証します。
10-4. 数値を検証してenumに変換する例
データベースやAPIから取得した数値を安全に変換する例です。
public static bool TryGetOrderStatus(int value,out OrderStatus status){if (Enum.IsDefined(typeof(OrderStatus),value)){status = (OrderStatus)value;return true;}status = OrderStatus.Unknown;return false;
}
使用例は次のとおりです。
int databaseValue = 3;if (TryGetOrderStatus(databaseValue,out OrderStatus status)){Console.WriteLine(status);}else{Console.WriteLine($"不正な値です: {databaseValue}");}
不正値を例外として扱う場合は、例外を投げるメソッドにすることもできます。
public static OrderStatus ConvertOrderStatus(int value){if (!Enum.IsDefined(typeof(OrderStatus),value)){throw new ArgumentOutOfRangeException(nameof(value),value,"定義されていない注文状態です。");}return (OrderStatus)value;
}
10-5. Flags属性で権限を管理する例
ユーザー権限をFlags属性付きenumで管理します。
[Flags]public enum UserPermission{None = 0,View = 1 << 0,Create = 1 << 1,Edit = 1 << 2,Delete = 1 << 3,Editor = View | Create | Edit,All = View | Create | Edit | Delete
}
編集者の権限を設定します。
UserPermission permission =UserPermission.Editor;削除権限があるか確認します。
if (permission.HasFlag(UserPermission.Delete)){Console.WriteLine("削除できます。");}else{Console.WriteLine("削除権限がありません。");}削除権限を追加します。
permission |= UserPermission.Delete;編集権限を解除します。
permission &= ~UserPermission.Edit;権限チェックをメソッドへまとめると、処理の意図が明確になります。
public static bool CanEdit(UserPermission permission){return permission.HasFlag(UserPermission.Edit);}11. C#のenumに関するよくある質問
11-1. enumのデフォルト値は何ですか
enumのデフォルト値は、基になる整数値が0の値です。
OrderStatus status = default;Console.WriteLine((int)status); // 0
0に対応するメンバーが定義されていなくても、デフォルト値は0です。
そのため、通常はUnknownやNoneなど、0に対応するメンバーを定義します。
public enum OrderStatus{Unknown = 0,Pending = 1,Shipped = 2}11-2. enumに文字列を直接設定できますか
enumのメンバーに文字列を直接割り当てることはできません。
次のような定義はエラーになります。
public enum OrderStatus{Pending = "受付待ち"}enumの基になる型として指定できるのは整数型だけです。
表示用の文字列が必要な場合は、次の方法を使用します。
switch式
Description属性Display属性辞書
拡張メソッド
リソースファイル
多言語対応が必要な場合は、属性へ固定の日本語を直接書くよりも、リソースファイルによるローカライズを検討しましょう。
11-3. enumにメソッドやプロパティを定義できますか
enum本体に、通常のクラスのようなインスタンスメソッドやプロパティを定義することはできません。
次のような定義はできません。
public enum OrderStatus{Pending,Shipped;public string GetDisplayName(){return "";}
}
処理を追加したい場合は、拡張メソッドを使用できます。
public static class OrderStatusExtensions{public static bool IsFinished(this OrderStatus status){return status isOrderStatus.Completed orOrderStatus.Canceled;}}次のように呼び出せます。
bool isFinished =OrderStatus.Completed.IsFinished();ただし、メンバーごとに複雑な状態や振る舞いが必要なら、enumではなくクラスで表現する設計も検討しましょう。
11-4. enumが定義済みか判定するにはどうすればよいですか
数値や値が定義済みか判定するには、Enum.IsDefinedを使用します。
bool isDefined =Enum.IsDefined(typeof(OrderStatus),3);文字列を変換しながら判定する場合は、Enum.TryParseと組み合わせます。
bool isValid =Enum.TryParse<OrderStatus>(input,ignoreCase: true,out OrderStatus status)&&Enum.IsDefined(typeof(OrderStatus),status);Flags属性付きenumでは、正しい組み合わせでもEnum.IsDefinedがfalseになることがあります。Flagsの場合は、不明なビットが含まれていないかを確認します。
bool isValid =(permission & ~Permission.All) == 0;11-5. enumをJSONで文字列として出力するにはどうすればよいですか
System.Text.Jsonを使用する場合は、JsonStringEnumConverterを設定します。
個別のenumに属性を付ける例は次のとおりです。
using System.Text.Json.Serialization;[JsonConverter(typeof(JsonStringEnumConverter))]public enum OrderStatus{Unknown = 0,Pending = 1,Shipped = 2}
シリアライズ処理のオプションに設定することもできます。
using System.Text.Json;using System.Text.Json.Serialization;JsonSerializerOptions options = new();
options.Converters.Add(new JsonStringEnumConverter());
string json =JsonSerializer.Serialize(OrderStatus.Shipped,options);
Console.WriteLine(json);// "Shipped"
ASP.NET Coreで全体設定を行う場合は、JSONオプションへコンバーターを追加します。
builder.Services.AddControllers().AddJsonOptions(options =>{options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());});文字列化するとJSONの可読性は高くなりますが、メンバー名の変更がAPI互換性に影響する点に注意してください。
11-6. enumとintのどちらを使うべきですか
アプリケーション内部で、特定の状態や種類を表す場合はenumが適しています。
void UpdateStatus(OrderStatus status){}intをそのまま使用すると、値の意味や使用可能な範囲が分かりにくくなります。
void UpdateStatus(int status){}一方、データベース、ファイル、通信データなどでは、enumの値をintとして保存する場合があります。
基本的には、境界部分でintとenumを変換し、アプリケーション内部ではenumとして扱う設計が分かりやすいでしょう。
int databaseValue = 3;if (!Enum.IsDefined(typeof(OrderStatus),databaseValue)){throw new InvalidOperationException("不正な注文状態です。");}
OrderStatus status =(OrderStatus)databaseValue;
まとめ
C#のenumは、関連する複数の定数を、意味のある名前でまとめて管理するための列挙型です。
数値や文字列を直接使う場合と比べて、コードの可読性と型安全性を高められます。
基本的な定義方法は次のとおりです。
public enum OrderStatus{Unknown = 0,Pending = 1,Processing = 2,Shipped = 3,Completed = 4,Canceled = 5}enumからintへの変換にはキャストを使用します。
int value = (int)OrderStatus.Shipped;intからenumへ変換する場合は、未定義の値でもキャストできるため、Enum.IsDefinedによる検証が重要です。
if (Enum.IsDefined(typeof(OrderStatus),value)){OrderStatus status =(OrderStatus)value;}文字列から安全に変換する場合は、Enum.TryParseを使用します。
if (Enum.TryParse<OrderStatus>(input,ignoreCase: true,out OrderStatus status) &&Enum.IsDefined(typeof(OrderStatus),status)){Console.WriteLine(status);}複数の値を組み合わせる場合は、Flags属性とビット演算を使用します。
[Flags]public enum Permission{None = 0,Read = 1,Write = 2,Delete = 4}enumを使用するときは、0に対応するメンバーを定義し、外部入力を検証し、保存形式やJSON形式を事前に決めておくことが大切です。
選択肢が固定的で単純な場合はenum、種類が頻繁に増えたり複雑な振る舞いを持ったりする場合は、クラスやデータベースによる別の設計を検討しましょう。

