C# enum(列挙型)とは?使い方・初期値・Flags・文字列変換まで初心者向けに徹底解説
はじめに
C#で開発をしていると、状態・種類・区分・権限など、あらかじめ決まった選択肢をコードで表現したい場面がよくあります。
たとえば、注文ステータスに「未処理」「処理中」「発送済み」「キャンセル済み」がある場合、文字列や数値で管理することもできます。
C#string status = "Shipped";
int statusCode = 2;
しかし、文字列や数値だけでは意味が分かりにくく、タイプミスや不正な値が入りやすくなります。
そこで便利なのが、C#のenum、つまり列挙型です。
C#OrderStatus status = OrderStatus.Shipped;
enumを使うと、決められた値の中から選択できるようになり、コードの可読性や保守性が高まります。
この記事では、C# enumの基本的な使い方から、初期値、数値変換、文字列変換、Flags属性、実践的な設計ポイントまで、初心者にも分かりやすく解説します。
1. C#のenum(列挙型)とは?
1-1. enumの基本概念と使う目的
C#のenumは、関連する定数の集合に名前を付けて扱うための型です。
正式には「列挙型」と呼ばれます。
たとえば、ユーザーの種類を表す場合、次のように定義できます。
C#enum UserRole
{
Guest,
Member,
Admin
}
このUserRoleには、Guest、Member、Adminという3つの値があります。
使うときは次のように書きます。
C#UserRole role = UserRole.Admin;
enumを使う目的は、主に次のようなものです。
決まった選択肢を型として表現する
数値や文字列の意味を分かりやすくする
不正な値の混入を減らす
switch文などで分岐処理を書きやすくするコードの保守性を高める
C# enumは、状態や種類を扱う場面で非常によく使われる基本機能です。
1-2. enumを使うとコードが読みやすくなる理由
enumを使うと、値の意味が名前として表現されるため、コードを読んだときに意図が分かりやすくなります。
たとえば、次のコードを見てみましょう。
C#int status = 2;
if (status == 2)
{
Console.WriteLine("発送済みです");
}
このコードでは、2が何を意味しているのか、コードだけでは分かりません。
一方、enumを使うと次のように書けます。
C#OrderStatus status = OrderStatus.Shipped;
if (status == OrderStatus.Shipped)
{
Console.WriteLine("発送済みです");
}
OrderStatus.Shippedと書かれていれば、「注文ステータスが発送済みである」とすぐに理解できます。
このように、enumはマジックナンバーを避け、コードの意味を明確にするために役立ちます。
1-3. enumを使わない場合との違い
enumを使わずに数値や文字列で状態を管理すると、次のような問題が起きやすくなります。
C#string status = "shiped";
本来は"shipped"と書きたかったのに、スペルミスで"shiped"になってしまいました。
文字列で管理している場合、このようなタイプミスはコンパイル時に検出されません。
また、数値で管理する場合も問題があります。
C#int status = 99;
99が有効なステータスなのかどうか、コードだけでは分かりません。
enumを使えば、次のように定義済みの名前を使って値を扱えます。
C#OrderStatus status = OrderStatus.Shipped;
これにより、コード補完が効きやすくなり、間違った値を使いにくくなります。
ただし、後述するように、C#では不正な数値をenumにキャストできてしまう点には注意が必要です。
1-4. enumがよく使われる場面
C# enumは、次のような場面でよく使われます。
ユーザー権限を表す場合です。
C#enum UserRole
{
Guest,
User,
Admin
}
注文ステータスを表す場合です。
C#enum OrderStatus
{
Pending,
Processing,
Shipped,
Cancelled
}
処理結果を表す場合です。
C#enum ResultStatus
{
Success,
Failed,
NotFound,
Unauthorized
}
曜日やカテゴリを表す場合にも使えます。
C#enum Category
{
Food,
Book,
Electronics,
Clothing
}
このように、選択肢が固定されていて、値に意味のある名前を付けたい場合にenumは非常に便利です。
2. C# enumの基本的な書き方
2-1. enumの定義方法
C#でenumを定義する基本形は次のとおりです。
C#enum EnumName
{
Value1,
Value2,
Value3
}
具体例として、注文ステータスを表すenumを定義してみます。
C#enum OrderStatus
{
Pending,
Processing,
Shipped,
Cancelled
}
enum名には、通常、単数形の名詞を使います。
C#enum OrderStatus
値の名前には、PascalCaseを使うのが一般的です。
C#Pending
Processing
Shipped
Cancelled
C#では、enumの各値は内部的には数値として扱われます。特に指定しなければ、最初の値が0、次が1、その次が2というように自動的に割り当てられます。
2-2. enum値の宣言と参照方法
定義したenumは、型として変数に使うことができます。
C#OrderStatus status = OrderStatus.Pending;
OrderStatus.Pendingのように、enum名.値名の形式で参照します。
値を比較することもできます。
C#if (status == OrderStatus.Pending)
{
Console.WriteLine("注文は未処理です");
}
メソッドの引数として使うこともできます。
C#void PrintStatus(OrderStatus status)
{
Console.WriteLine(status);
}
PrintStatus(OrderStatus.Shipped);
このように、enumは通常の型と同じように、変数、引数、戻り値などに使えます。
C#OrderStatus GetDefaultStatus()
{
return OrderStatus.Pending;
}
enumを使うことで、メソッドが受け取れる値の意味が明確になります。
2-3. switch文でenumを使う方法
enumはswitch文との相性が非常に良いです。
C#OrderStatus status = OrderStatus.Shipped;
switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("注文を受け付けました");
break;
case OrderStatus.Processing:
Console.WriteLine("処理中です");
break;
case OrderStatus.Shipped:
Console.WriteLine("発送済みです");
break;
case OrderStatus.Cancelled:
Console.WriteLine("キャンセルされました");
break;
default:
Console.WriteLine("不明なステータスです");
break;
}
switch文でenumを使うと、値ごとの処理を整理しやすくなります。
C#のバージョンによっては、switch式を使ってより簡潔に書くこともできます。
C#string message = status switch
{
OrderStatus.Pending => "注文を受け付けました",
OrderStatus.Processing => "処理中です",
OrderStatus.Shipped => "発送済みです",
OrderStatus.Cancelled => "キャンセルされました",
_ => "不明なステータスです"
};
Console.WriteLine(message);
enumの値ごとに表示文言や処理内容を切り替えたい場合、switchはよく使われる書き方です。
2-4. クラス内・名前空間内でのenum定義
enumは、名前空間の中に定義することも、クラスの中に定義することもできます。
名前空間内に定義する例です。
C#namespace MyApp
{
enum OrderStatus
{
Pending,
Processing,
Shipped,
Cancelled
}
}
複数のクラスから使う共通のenumであれば、名前空間内に定義することが多いです。
一方、特定のクラスの中でだけ使うenumであれば、クラス内に定義することもできます。
C#class Order
{
public enum Status
{
Pending,
Processing,
Shipped,
Cancelled
}
public Status CurrentStatus { get; set; }
}
使うときは次のように参照します。
C#Order.Status status = Order.Status.Shipped;
クラス内に定義すると、Orderに関連する状態であることが明確になります。
ただし、他のクラスからも頻繁に使う場合は、独立したenumとして定義した方が扱いやすいこともあります。
3. enumの値と初期値の仕組み
3-1. enumのデフォルト値は0
C# enumの重要なポイントとして、デフォルト値は0であるという点があります。
たとえば、次のようなenumを定義したとします。
C#enum OrderStatus
{
Pending,
Processing,
Shipped,
Cancelled
}
この場合、内部的な数値は次のようになります。
C#Pending = 0
Processing = 1
Shipped = 2
Cancelled = 3
defaultを使うと、enumのデフォルト値は0になります。
C#OrderStatus status = default;
Console.WriteLine(status); // Pending
最初の値であるPendingが0なので、defaultの結果はOrderStatus.Pendingになります。
C# enumを設計するときは、この「デフォルト値が0」という仕様を意識することが大切です。
3-2. 明示的に数値を割り当てる方法
enumの各値には、明示的に数値を割り当てることもできます。
C#enum OrderStatus
{
Pending = 1,
Processing = 2,
Shipped = 3,
Cancelled = 4
}
このように書くと、Pendingは1、Processingは2になります。
数値に意味がある場合や、外部システム・データベース・APIと値を合わせたい場合に使われます。
C#enum PaymentStatus
{
Unpaid = 10,
Paid = 20,
Refunded = 30
}
ただし、明示的な数値を割り当てる場合は、あとから変更しにくくなる点に注意が必要です。
特に、データベースに保存している値やAPIで公開している値を変更すると、既存データや外部連携に影響する可能性があります。
3-3. 途中から連番になる仕組み
enumでは、一部の値だけに数値を指定すると、その後の値は自動的に連番になります。
C#enum OrderStatus
{
Pending = 1,
Processing,
Shipped,
Cancelled
}
この場合、値は次のようになります。
C#Pending = 1
Processing = 2
Shipped = 3
Cancelled = 4
途中で別の数値を指定することもできます。
C#enum SampleStatus
{
None = 0,
First = 10,
Second,
Third = 20,
Fourth
}
この場合は次のようになります。
C#None = 0
First = 10
Second = 11
Third = 20
Fourth = 21
この仕組みを理解しておくと、意図しない数値が割り当てられるミスを防げます。
外部システムと数値を合わせる必要がある場合は、すべての値に明示的な数値を指定すると安全です。
3-4. 0に対応する値を定義すべき理由
C# enumでは、0に対応する値を定義しておくことが推奨されます。
理由は、enumのデフォルト値が0だからです。
たとえば、次のような定義を考えます。
C#enum OrderStatus
{
Pending = 1,
Processing = 2,
Shipped = 3
}
この場合、0に対応する名前がありません。
C#OrderStatus status = default;
Console.WriteLine(status); // 0
defaultの結果は0ですが、対応する名前がないため、表示すると0になります。
これは分かりにくく、バグの原因になることがあります。
そのため、次のようにNoneやUnknownを0として定義しておくと安全です。
C#enum OrderStatus
{
None = 0,
Pending = 1,
Processing = 2,
Shipped = 3,
Cancelled = 4
}
未設定状態を表したい場合はNone、不明な状態を表したい場合はUnknownなど、意味に合った名前を付けるとよいでしょう。
C#enum UserRole
{
None = 0,
Guest = 1,
Member = 2,
Admin = 3
}
0の値を定義しておくことで、初期化漏れや未設定状態を扱いやすくなります。
3-5. enumの基になる型を変更する方法
C# enumの内部的な型は、デフォルトではintです。
しかし、必要に応じて基になる型を変更できます。
C#enum StatusCode : byte
{
None = 0,
Success = 1,
Failed = 2
}
指定できる型には、主に次のような整数型があります。
C#byte
sbyte
short
ushort
int
uint
long
ulong
たとえば、非常に小さな値だけを扱う場合はbyteを使うこともできます。
C#enum SmallStatus : byte
{
None = 0,
Active = 1,
Inactive = 2
}
一方、大きな値やビットフラグを扱う場合は、longやulongを使うこともあります。
C#[Flags]
enum LargePermission : long
{
None = 0,
Read = 1L << 0,
Write = 1L << 1,
Delete = 1L << 2
}
通常のアプリケーション開発では、特別な理由がなければデフォルトのintで問題ありません。
4. enumと数値の変換方法
4-1. enumからintへ変換する
enumは内部的には数値なので、キャストによってintに変換できます。
C#enum OrderStatus
{
None = 0,
Pending = 1,
Processing = 2,
Shipped = 3
}
C#OrderStatus status = OrderStatus.Shipped;
int value = (int)status;
Console.WriteLine(value); // 3
このように、(int)を付けることでenumの数値を取得できます。
基になる型がbyteの場合は、byteにキャストできます。
C#enum SmallStatus : byte
{
None = 0,
Active = 1,
Inactive = 2
}
SmallStatus status = SmallStatus.Active;
byte value = (byte)status;
Console.WriteLine(value); // 1
APIやデータベースに数値として保存したい場合に、この変換が使われます。
4-2. intからenumへ変換する
数値からenumへ変換する場合も、キャストを使います。
C#int value = 3;
OrderStatus status = (OrderStatus)value;
Console.WriteLine(status); // Shipped
この例では、3がOrderStatus.Shippedに対応しているため、Shippedと表示されます。
ただし、ここで重要なのは、定義されていない数値でもenumにキャストできてしまうという点です。
C#int value = 99;
OrderStatus status = (OrderStatus)value;
Console.WriteLine(status); // 99
OrderStatusに99という値を定義していなくても、キャスト自体は成功します。
そのため、外部から受け取った数値をenumに変換する場合は、有効な値かどうかを確認する必要があります。
4-3. Enum.IsDefinedで有効な値か判定する
数値がenumに定義されているかどうかを確認するには、Enum.IsDefinedを使います。
C#int value = 3;
if (Enum.IsDefined(typeof(OrderStatus), value))
{
OrderStatus status = (OrderStatus)value;
Console.WriteLine(status);
}
else
{
Console.WriteLine("不正な値です");
}
valueがOrderStatusに定義されている値であれば、trueになります。
ジェネリックを使える環境では、次のように書くこともできます。
C#int value = 3;
if (Enum.IsDefined<OrderStatus>((OrderStatus)value))
{
OrderStatus status = (OrderStatus)value;
Console.WriteLine(status);
}
Enum.IsDefinedを使うことで、想定外の数値がenumとして扱われるのを防ぎやすくなります。
4-4. 不正な数値をenumに変換したときの注意点
C#では、次のようなコードがコンパイルできてしまいます。
C#OrderStatus status = (OrderStatus)999;
この値はOrderStatusに定義されていなくても、変数には入ります。
そのため、次のようなswitch文ではdefaultに入ります。
C#switch (status)
{
case OrderStatus.None:
Console.WriteLine("未設定");
break;
case OrderStatus.Pending:
Console.WriteLine("未処理");
break;
case OrderStatus.Processing:
Console.WriteLine("処理中");
break;
case OrderStatus.Shipped:
Console.WriteLine("発送済み");
break;
default:
Console.WriteLine("未対応の値です");
break;
}
外部入力、データベース、API、JSONなどから値を受け取る場合は、必ず検証処理を入れることが重要です。
C#int input = 999;
if (!Enum.IsDefined(typeof(OrderStatus), input))
{
throw new ArgumentException("不正な注文ステータスです");
}
OrderStatus status = (OrderStatus)input;
C# enumは便利ですが、数値変換時には「定義されていない値も入る可能性がある」という点を忘れないようにしましょう。
5. enumと文字列の変換方法
5-1. enumを文字列に変換するToStringの使い方
enumを文字列に変換するには、ToString()を使います。
C#OrderStatus status = OrderStatus.Shipped;
string text = status.ToString();
Console.WriteLine(text); // Shipped
ToString()を使うと、enumの名前が文字列として取得できます。
ログ出力や画面表示、デバッグなどでよく使われます。
C#Console.WriteLine($"現在のステータス: {status}");
文字列補間でも自動的にToString()が呼ばれるため、同じようにShippedと表示されます。
ただし、ToString()で得られる文字列は、enumに定義した名前そのものです。
C#OrderStatus.Processing.ToString(); // "Processing"
日本語表示にしたい場合は、別途変換処理を用意する必要があります。
5-2. 文字列からenumへ変換するEnum.Parse
文字列からenumへ変換するには、Enum.Parseを使います。
C#string text = "Shipped";
OrderStatus status = (OrderStatus)Enum.Parse(typeof(OrderStatus), text);
Console.WriteLine(status); // Shipped
ジェネリック版を使うと、より簡潔に書けます。
C#OrderStatus status = Enum.Parse<OrderStatus>("Shipped");
Console.WriteLine(status); // Shipped
ただし、存在しない文字列を指定すると例外が発生します。
C#OrderStatus status = Enum.Parse<OrderStatus>("UnknownValue");
この場合、実行時に例外が発生します。
そのため、ユーザー入力や外部データを変換する場合は、Enum.ParseよりもEnum.TryParseを使う方が安全です。
5-3. 安全に変換するEnum.TryParse
Enum.TryParseを使うと、変換に失敗しても例外を発生させずに処理できます。
C#string text = "Shipped";
if (Enum.TryParse<OrderStatus>(text, out OrderStatus status))
{
Console.WriteLine($"変換成功: {status}");
}
else
{
Console.WriteLine("変換に失敗しました");
}
TryParseは、変換に成功するとtrue、失敗するとfalseを返します。
ユーザー入力やJSON、設定ファイルなど、正しい値が入っているとは限らない場合に便利です。
C#string input = "InvalidStatus";
if (!Enum.TryParse<OrderStatus>(input, out OrderStatus status))
{
Console.WriteLine("不正なステータスです");
}
ただし、TryParseにも注意点があります。
文字列が数値の場合、定義されていない数値でも変換できることがあります。
C#Enum.TryParse<OrderStatus>("999", out OrderStatus status);
Console.WriteLine(status); // 999
そのため、厳密にチェックしたい場合は、TryParseの後にEnum.IsDefinedを組み合わせると安全です。
C#string input = "999";
if (Enum.TryParse<OrderStatus>(input, out OrderStatus status)
&& Enum.IsDefined(typeof(OrderStatus), status))
{
Console.WriteLine(status);
}
else
{
Console.WriteLine("不正な値です");
}
5-4. 大文字・小文字を無視して変換する方法
Enum.ParseやEnum.TryParseでは、大文字・小文字を無視して変換することもできます。
C#string text = "shipped";
OrderStatus status = Enum.Parse<OrderStatus>(text, ignoreCase: true);
Console.WriteLine(status); // Shipped
TryParseの場合は次のように書きます。
C#string text = "shipped";
if (Enum.TryParse<OrderStatus>(text, ignoreCase: true, out OrderStatus status))
{
Console.WriteLine(status); // Shipped
}
外部から受け取る文字列では、"Shipped"、"shipped"、"SHIPPED"のように表記が揺れることがあります。
そのような場合は、ignoreCase: trueを指定すると扱いやすくなります。
ただし、表記ゆれをどこまで許容するかは、アプリケーションの設計方針によって決めるべきです。
厳密なAPIでは、大文字・小文字も含めて仕様として固定することがあります。
5-5. 表示名を日本語にしたい場合の対応方法
enumの名前は、通常英語で定義します。
C#enum OrderStatus
{
None,
Pending,
Processing,
Shipped,
Cancelled
}
しかし、画面には日本語で表示したい場合があります。
その場合、switch式で表示名を変換する方法が分かりやすいです。
C#static string ToDisplayName(OrderStatus status)
{
return status switch
{
OrderStatus.None => "未設定",
OrderStatus.Pending => "未処理",
OrderStatus.Processing => "処理中",
OrderStatus.Shipped => "発送済み",
OrderStatus.Cancelled => "キャンセル済み",
_ => "不明"
};
}
使い方は次のとおりです。
C#OrderStatus status = OrderStatus.Shipped;
Console.WriteLine(ToDisplayName(status)); // 発送済み
属性を使って表示名を持たせる方法もあります。
C#using System.ComponentModel.DataAnnotations;
enum OrderStatus
{
[Display(Name = "未設定")]
None,
[Display(Name = "未処理")]
Pending,
[Display(Name = "処理中")]
Processing,
[Display(Name = "発送済み")]
Shipped,
[Display(Name = "キャンセル済み")]
Cancelled
}
ただし、属性から値を取得するにはリフレクションなどの処理が必要です。
初心者のうちは、まずswitch式で変換する方法を覚えるとよいでしょう。
6. Flags属性を使ったビットフラグ
6-1. Flags属性とは?
Flags属性は、enumの値をビットフラグとして扱うための属性です。
通常のenumは、基本的に1つの値を表します。
C#UserRole role = UserRole.Admin;
一方、Flagsを使うと、複数の値を組み合わせて持つことができます。
たとえば、ユーザー権限として「読み取り」「書き込み」「削除」を組み合わせたい場合に便利です。
C#[Flags]
enum Permission
{
None = 0,
Read = 1,
Write = 2,
Delete = 4
}
このように定義すると、次のように複数の権限を組み合わせられます。
C#Permission permission = Permission.Read | Permission.Write;
Flags属性は、複数選択できる状態を表現するときに使います。
6-2. ビットフラグに適したenum値の定義方法
Flagsを使う場合、値は1, 2, 4, 8, 16のように2の累乗で定義します。
C#[Flags]
enum Permission
{
None = 0,
Read = 1,
Write = 2,
Delete = 4,
Execute = 8
}
2の累乗で定義する理由は、ビット単位で値を組み合わせるためです。
C#Read = 0001
Write = 0010
Delete = 0100
Execute = 1000
ReadとWriteを組み合わせると、次のようになります。
C#Read | Write = 0011
それぞれのビットが重ならないため、どの権限を持っているか判定できます。
より分かりやすくするために、ビットシフトで定義することもあります。
C#[Flags]
enum Permission
{
None = 0,
Read = 1 << 0, // 1
Write = 1 << 1, // 2
Delete = 1 << 2, // 4
Execute = 1 << 3 // 8
}
Flagsを使う場合は、通常の連番である0, 1, 2, 3のように定義しないように注意しましょう。
6-3. 複数の値を組み合わせる方法
複数のenum値を組み合わせるには、ビットOR演算子|を使います。
C#Permission permission = Permission.Read | Permission.Write;
Console.WriteLine(permission); // Read, Write
さらに権限を追加したい場合も、|を使います。
C#permission = permission | Permission.Delete;
省略して次のように書くこともできます。
C#permission |= Permission.Delete;
権限を削除したい場合は、ビットANDとビットNOTを組み合わせます。
C#permission &= ~Permission.Write;
これは「Write以外を残す」という意味です。
C#Console.WriteLine(permission);
Flagsを使うと、複数の状態や権限を1つの変数で管理できます。
6-4. HasFlagで値を判定する方法
特定のフラグを持っているかどうかを確認するには、HasFlagを使えます。
C#Permission permission = Permission.Read | Permission.Write;
if (permission.HasFlag(Permission.Read))
{
Console.WriteLine("読み取り権限があります");
}
Writeを持っているかも確認できます。
C#if (permission.HasFlag(Permission.Write))
{
Console.WriteLine("書き込み権限があります");
}
Deleteを持っていない場合は、次の条件はfalseになります。
C#if (permission.HasFlag(Permission.Delete))
{
Console.WriteLine("削除権限があります");
}
ビット演算で判定する方法もあります。
C#if ((permission & Permission.Read) == Permission.Read)
{
Console.WriteLine("読み取り権限があります");
}
初心者にはHasFlagの方が読みやすいですが、パフォーマンスを重視する場面ではビット演算を使うこともあります。
6-5. Flagsを使うときの注意点
Flagsを使うときは、いくつか注意点があります。
まず、値は必ず2の累乗で定義しましょう。
悪い例です。
C#[Flags]
enum Permission
{
None = 0,
Read = 1,
Write = 2,
Delete = 3
}
Delete = 3は、ビットで見るとRead | Writeと同じ意味になってしまいます。
正しい例です。
C#[Flags]
enum Permission
{
None = 0,
Read = 1,
Write = 2,
Delete = 4
}
また、None = 0を定義しておくことも重要です。
C#Permission permission = Permission.None;
何も権限を持っていない状態を明確に表せます。
さらに、複数の値をまとめた便利な値を定義することもあります。
C#[Flags]
enum Permission
{
None = 0,
Read = 1,
Write = 2,
Delete = 4,
All = Read | Write | Delete
}
ただし、Allを定義する場合は、将来フラグを追加したときにAllも更新する必要があります。
7. enumの便利な操作方法
7-1. enumの全要素を取得する
enumの全要素を取得するには、Enum.GetValuesを使います。
C#foreach (OrderStatus status in Enum.GetValues(typeof(OrderStatus)))
{
Console.WriteLine(status);
}
出力例です。
C#None
Pending
Processing
Shipped
Cancelled
ジェネリック版を使える場合は、次のように書けます。
C#foreach (OrderStatus status in Enum.GetValues<OrderStatus>())
{
Console.WriteLine(status);
}
Enum.GetValuesは、画面のドロップダウンリストを作るときや、すべての状態をチェックしたいときに便利です。
C#var statuses = Enum.GetValues<OrderStatus>();
取得した値を使って、選択肢を自動生成することもできます。
7-2. enumの名前一覧を取得する
enumの名前だけを文字列として取得したい場合は、Enum.GetNamesを使います。
C#string[] names = Enum.GetNames(typeof(OrderStatus));
foreach (string name in names)
{
Console.WriteLine(name);
}
出力例です。
C#None
Pending
Processing
Shipped
Cancelled
ジェネリック版を使える場合は、次のように書けます。
C#string[] names = Enum.GetNames<OrderStatus>();
名前一覧は、ログ出力、設定画面、管理画面などで使えます。
ただし、表示用に日本語名が必要な場合は、Enum.GetNamesで取得した英語名をそのまま表示するのではなく、表示名への変換処理を用意した方がよいでしょう。
7-3. enumの値一覧をループ処理する
Enum.GetValuesを使うと、enumの値一覧をループできます。
C#foreach (OrderStatus status in Enum.GetValues<OrderStatus>())
{
Console.WriteLine($"{(int)status}: {status}");
}
出力例です。
C#0: None
1: Pending
2: Processing
3: Shipped
4: Cancelled
画面表示用に加工する例です。
C#foreach (OrderStatus status in Enum.GetValues<OrderStatus>())
{
string displayName = ToDisplayName(status);
Console.WriteLine(displayName);
}
enumの値をすべて処理したい場合、手動で配列を書くよりもEnum.GetValuesを使う方が保守しやすくなります。
値を追加したときにも、自動的にループ対象に含まれるためです。
7-4. ジェネリックを使ったenum操作
C#では、ジェネリックを使ってenumを扱う共通メソッドを作ることもできます。
たとえば、文字列から安全にenumへ変換するメソッドです。
C#static bool TryParseEnum<TEnum>(string value, out TEnum result)
where TEnum : struct, Enum
{
return Enum.TryParse<TEnum>(value, ignoreCase: true, out result);
}
使い方です。
C#if (TryParseEnum<OrderStatus>("shipped", out var status))
{
Console.WriteLine(status);
}
有効な値かどうかまで確認したい場合は、次のようにできます。
C#static bool TryParseDefinedEnum<TEnum>(string value, out TEnum result)
where TEnum : struct, Enum
{
if (Enum.TryParse<TEnum>(value, ignoreCase: true, out result)
&& Enum.IsDefined(typeof(TEnum), result))
{
return true;
}
result = default;
return false;
}
複数のenumで同じような変換処理が必要な場合、ジェネリックメソッドにしておくと再利用しやすくなります。
7-5. nullを扱いたい場合のNullable enum
通常のenumは値型なので、nullを代入できません。
C#OrderStatus status = null; // エラー
nullを扱いたい場合は、Nullable enumを使います。
C#OrderStatus? status = null;
これは次の書き方と同じ意味です。
C#Nullable<OrderStatus> status = null;
値があるかどうかは、HasValueで確認できます。
C#OrderStatus? status = OrderStatus.Pending;
if (status.HasValue)
{
Console.WriteLine(status.Value);
}
より簡単に、nullチェックで書くこともできます。
C#if (status != null)
{
Console.WriteLine(status);
}
未選択状態や、外部データで値が存在しない可能性がある場合に、Nullable enumは便利です。
C#OrderStatus? selectedStatus = null;
ただし、未設定を表すだけであれば、None = 0を定義する方が分かりやすい場合もあります。
nullとNoneのどちらを使うかは、意味を分けて考えることが大切です。
8. enumを使うときの設計ポイント
8-1. enumを使うべきケース
enumは、値の候補が決まっている場合に向いています。
たとえば、次のようなケースです。
C#enum OrderStatus
{
None = 0,
Pending = 1,
Processing = 2,
Shipped = 3,
Cancelled = 4
}
注文ステータスのように、アプリケーション内で扱う状態が明確に決まっている場合はenumが適しています。
ほかにも、次のようなケースで使いやすいです。
C#enum Gender
{
Unknown = 0,
Male = 1,
Female = 2,
Other = 3
}
C#enum PaymentMethod
{
None = 0,
CreditCard = 1,
BankTransfer = 2,
Cash = 3
}
C#enum LogLevel
{
Trace,
Debug,
Information,
Warning,
Error,
Critical
}
選択肢が少なく、頻繁に増減しない値であれば、C# enumはシンプルで扱いやすい選択肢です。
8-2. enumよりクラスや定数が向いているケース
すべての固定値をenumにすればよいわけではありません。
値に追加情報を持たせたい場合は、enumよりクラスの方が向いていることがあります。
たとえば、商品カテゴリに「表示名」「説明」「並び順」「有効・無効」などの情報を持たせたい場合です。
C#class Category
{
public int Id { get; set; }
public string Name { get; set; } = "";
public int DisplayOrder { get; set; }
}
また、値がデータベースで管理され、管理画面から追加・変更されるような場合も、enumではなくマスタテーブルとして扱う方が適しています。
enumが向いていない例です。
ユーザーが自由に追加できるカテゴリ
DBで管理するマスタデータ
表示名や説明文など属性が多い値
外部サービスによって頻繁に値が増えるもの
ビジネスルールが複雑に紐づくもの
単純な固定選択肢ならenum、データとして管理したいならクラスやDBを使う、と考えると分かりやすいです。
8-3. 命名規則と可読性のポイント
enumの名前は、意味が明確になるように付けることが大切です。
良い例です。
C#enum OrderStatus
{
None,
Pending,
Processing,
Shipped,
Cancelled
}
悪い例です。
C#enum Status
{
A,
B,
C
}
Statusだけでは何の状態なのか分かりにくく、AやBも意味が不明です。
より具体的に、何を表すenumなのか分かる名前にしましょう。
C#enum UserRole
enum PaymentStatus
enum ShippingMethod
enum ErrorCode
値の名前も、略しすぎない方が読みやすくなります。
C#enum PaymentStatus
{
None,
Unpaid,
Paid,
Refunded,
Failed
}
C#では、enum名と値名にはPascalCaseを使うのが一般的です。
C#OrderStatus.Processing
UserRole.Admin
PaymentMethod.CreditCard
読みやすい名前を付けることで、コメントを書かなくても意味が伝わるコードになります。
8-4. 値の追加・変更時に注意すべきこと
enumの値を追加すること自体は簡単です。
C#enum OrderStatus
{
None = 0,
Pending = 1,
Processing = 2,
Shipped = 3,
Cancelled = 4,
Returned = 5
}
しかし、既存のenumに値を追加するときは、影響範囲を確認する必要があります。
特に注意すべきなのは、switch文です。
C#switch (status)
{
case OrderStatus.Pending:
break;
case OrderStatus.Processing:
break;
case OrderStatus.Shipped:
break;
case OrderStatus.Cancelled:
break;
}
ここにReturnedを追加しても、switch文側が対応していなければ、意図しない動作になる可能性があります。
また、数値を変更する場合はさらに注意が必要です。
C#Pending = 1
Processing = 2
Shipped = 3
このような値をDBに保存している場合、あとから数値を変更すると既存データの意味が変わってしまいます。
そのため、外部に保存・公開しているenumの数値は、基本的に変更しない方が安全です。
値を削除する場合も、既存データや古いAPIクライアントがその値を使っていないか確認しましょう。
8-5. APIやDBでenumを扱うときの注意点
APIやDBでenumを扱う場合、数値として保存するか、文字列として保存するかを考える必要があります。
数値で保存する例です。
C#OrderStatus status = OrderStatus.Shipped;
int value = (int)status;
メリットは、データ量が小さく、処理しやすいことです。
一方で、DBを直接見たときに3が何を意味するのか分かりにくいというデメリットがあります。
文字列で保存する例です。
C#string value = status.ToString(); // "Shipped"
メリットは、人間が読んで意味を理解しやすいことです。
一方で、enum名を変更すると、保存済みデータとの互換性に問題が出る可能性があります。
APIでは、数値で返すか文字列で返すかによって使いやすさが変わります。
JSON{
"status": 3
}
JSON{
"status": "Shipped"
}
一般的に、外部APIでは文字列の方が読みやすいですが、仕様として値の名前を安定させる必要があります。
DBやAPIでC# enumを扱う場合は、あとから変更しにくいことを前提に設計しましょう。
9. C# enumでよくあるエラーと対処法
9-1. 文字列変換で例外が発生する
Enum.Parseを使うと、存在しない文字列を指定したときに例外が発生します。
C#OrderStatus status = Enum.Parse<OrderStatus>("Invalid");
このコードは実行時に失敗します。
対処法としては、Enum.TryParseを使います。
C#string input = "Invalid";
if (Enum.TryParse<OrderStatus>(input, out OrderStatus status))
{
Console.WriteLine(status);
}
else
{
Console.WriteLine("不正なステータスです");
}
外部入力を扱う場合は、基本的にParseよりTryParseを使う方が安全です。
さらに厳密にチェックしたい場合は、Enum.IsDefinedも組み合わせます。
C#if (Enum.TryParse<OrderStatus>(input, out OrderStatus status)
&& Enum.IsDefined(typeof(OrderStatus), status))
{
Console.WriteLine(status);
}
else
{
Console.WriteLine("定義されていない値です");
}
これにより、存在しない名前や不正な数値を避けられます。
9-2. 想定外の数値がenumとして扱われる
C# enumでは、定義していない数値でもキャストできます。
C#OrderStatus status = (OrderStatus)999;
Console.WriteLine(status); // 999
この仕様を知らないと、「enumだから定義済みの値しか入らない」と誤解してしまうことがあります。
対処法は、外部から受け取った数値をそのまま使わず、検証することです。
C#int input = 999;
if (!Enum.IsDefined(typeof(OrderStatus), input))
{
Console.WriteLine("不正な値です");
return;
}
OrderStatus status = (OrderStatus)input;
特に、DB、API、フォーム入力、設定ファイルなどから値を取得する場合は注意しましょう。
アプリケーション内部で生成した値なら問題になりにくいですが、外部入力は常に不正な値の可能性があります。
9-3. switch文で未対応の値が発生する
enumに新しい値を追加したとき、既存のswitch文が対応していないことがあります。
C#enum OrderStatus
{
None,
Pending,
Processing,
Shipped,
Cancelled,
Returned
}
しかし、switch文が古いままだと、Returnedの処理が抜ける可能性があります。
C#switch (status)
{
case OrderStatus.Pending:
Console.WriteLine("未処理");
break;
case OrderStatus.Processing:
Console.WriteLine("処理中");
break;
case OrderStatus.Shipped:
Console.WriteLine("発送済み");
break;
case OrderStatus.Cancelled:
Console.WriteLine("キャンセル済み");
break;
default:
Console.WriteLine("未対応のステータスです");
break;
}
対処法として、defaultを用意しておくと安全です。
また、重要な処理では、未対応の値が来たときに例外を投げる設計もあります。
C#string message = status switch
{
OrderStatus.None => "未設定",
OrderStatus.Pending => "未処理",
OrderStatus.Processing => "処理中",
OrderStatus.Shipped => "発送済み",
OrderStatus.Cancelled => "キャンセル済み",
OrderStatus.Returned => "返品済み",
_ => throw new ArgumentOutOfRangeException(nameof(status), status, "未対応のステータスです")
};
新しいenum値を追加したら、関連するswitch文や変換処理も必ず確認しましょう。
9-4. Flagsの値が正しく判定できない
Flagsを使っているのに、判定がうまくいかない場合は、値の定義が間違っていることがあります。
悪い例です。
C#[Flags]
enum Permission
{
None = 0,
Read = 1,
Write = 2,
Delete = 3
}
Delete = 3は、ビット的にはRead | Writeと同じです。
そのため、意図しない判定結果になる可能性があります。
正しい例です。
C#[Flags]
enum Permission
{
None = 0,
Read = 1,
Write = 2,
Delete = 4
}
判定は次のように行います。
C#Permission permission = Permission.Read | Permission.Write;
if (permission.HasFlag(Permission.Read))
{
Console.WriteLine("読み取り可能");
}
または、ビット演算で判定します。
C#if ((permission & Permission.Read) == Permission.Read)
{
Console.WriteLine("読み取り可能");
}
Flagsでは、値を2の累乗にすること、None = 0を用意することが基本です。
9-5. JSON変換で文字列・数値の扱いに迷う
C# enumをJSONに変換するとき、数値として扱うか、文字列として扱うかで迷うことがあります。
数値の場合です。
JSON{
"status": 3
}
文字列の場合です。
JSON{
"status": "Shipped"
}
数値はコンパクトですが、意味が分かりにくいです。
文字列は読みやすいですが、名前を変更すると互換性に影響します。
ASP.NET CoreなどでSystem.Text.Jsonを使う場合、文字列として出力したいときはJsonStringEnumConverterを使うことがあります。
C#using System.Text.Json;
using System.Text.Json.Serialization;
var options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumConverter());
string json = JsonSerializer.Serialize(OrderStatus.Shipped, options);
Console.WriteLine(json); // "Shipped"
APIのレスポンスでは、利用者にとって分かりやすい文字列が選ばれることも多いです。
ただし、どちらを選ぶ場合でも、途中で形式を変えると互換性に影響するため、最初の設計が重要です。
10. C# enumの実践サンプル
10-1. ユーザー権限をenumで管理する例
ユーザーの権限をenumで管理する例です。
C#enum UserRole
{
None = 0,
Guest = 1,
Member = 2,
Admin = 3
}
ユーザー情報を表すクラスです。
C#class User
{
public string Name { get; set; } = "";
public UserRole Role { get; set; }
}
権限によって処理を分けます。
C#User user = new User
{
Name = "Taro",
Role = UserRole.Admin
};
if (user.Role == UserRole.Admin)
{
Console.WriteLine("管理者画面を表示します");
}
else
{
Console.WriteLine("通常画面を表示します");
}
switch式を使って表示名を取得することもできます。
C#static string GetRoleName(UserRole role)
{
return role switch
{
UserRole.None => "権限なし",
UserRole.Guest => "ゲスト",
UserRole.Member => "会員",
UserRole.Admin => "管理者",
_ => "不明"
};
}
このように、権限の種類が固定されている場合はenumで表現すると分かりやすくなります。
10-2. 注文ステータスをenumで管理する例
注文ステータスは、C# enumがよく使われる代表的な例です。
C#enum OrderStatus
{
None = 0,
Pending = 1,
Processing = 2,
Shipped = 3,
Cancelled = 4
}
注文クラスを作ります。
C#class Order
{
public int Id { get; set; }
public OrderStatus Status { get; set; }
}
ステータスに応じてメッセージを表示します。
C#static string GetOrderMessage(OrderStatus status)
{
return status switch
{
OrderStatus.None => "ステータスが未設定です",
OrderStatus.Pending => "注文を受け付けました",
OrderStatus.Processing => "注文を処理中です",
OrderStatus.Shipped => "商品を発送しました",
OrderStatus.Cancelled => "注文はキャンセルされました",
_ => "不明なステータスです"
};
}
使い方です。
C#Order order = new Order
{
Id = 1001,
Status = OrderStatus.Shipped
};
Console.WriteLine(GetOrderMessage(order.Status));
出力される内容です。
C#商品を発送しました
注文ステータスのように、業務上の状態を表す値にはenumが向いています。
10-3. 曜日やカテゴリをenumで表現する例
曜日やカテゴリもenumで表現できます。
曜日の例です。
C#enum Weekday
{
Sunday = 0,
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6
}
使い方です。
C#Weekday today = Weekday.Monday;
if (today == Weekday.Monday)
{
Console.WriteLine("週の始まりです");
}
カテゴリの例です。
C#enum ProductCategory
{
None = 0,
Food = 1,
Book = 2,
Electronics = 3,
Clothing = 4
}
カテゴリごとに表示名を変えることもできます。
C#static string GetCategoryName(ProductCategory category)
{
return category switch
{
ProductCategory.None => "未分類",
ProductCategory.Food => "食品",
ProductCategory.Book => "書籍",
ProductCategory.Electronics => "家電",
ProductCategory.Clothing => "衣類",
_ => "不明"
};
}
ただし、カテゴリを管理画面から追加・変更したい場合は、enumではなくDBのマスタテーブルで管理する方が適している場合があります。
10-4. Flagsで権限を複数持たせる例
ユーザーが複数の権限を持てる場合は、Flags属性を使うと便利です。
C#[Flags]
enum Permission
{
None = 0,
Read = 1,
Write = 2,
Delete = 4,
ManageUsers = 8,
All = Read | Write | Delete | ManageUsers
}
ユーザーに複数の権限を付与します。
C#Permission permission = Permission.Read | Permission.Write;
権限を判定します。
C#if (permission.HasFlag(Permission.Read))
{
Console.WriteLine("読み取り権限があります");
}
if (permission.HasFlag(Permission.Write))
{
Console.WriteLine("書き込み権限があります");
}
if (!permission.HasFlag(Permission.Delete))
{
Console.WriteLine("削除権限はありません");
}
権限を追加します。
C#permission |= Permission.Delete;
権限を削除します。
C#permission &= ~Permission.Write;
すべての権限を付与します。
C#permission = Permission.All;
このように、複数の値を組み合わせて管理したい場合は、通常のenumではなくFlags付きのenumを使うと表現しやすくなります。
11. C# enumに関するよくある質問
11-1. enumの初期値は必ず0ですか?
enum型のデフォルト値は0です。
ただし、最初のメンバーが必ず0でなければならないわけではありません。
C#enum Status
{
Active = 1,
Inactive = 2
}
このように定義することもできます。
しかし、default(Status)は0になります。
C#Status status = default;
Console.WriteLine(status); // 0
0に対応する名前がないと分かりにくいため、通常はNone = 0やUnknown = 0を定義しておくのがおすすめです。
C#enum Status
{
None = 0,
Active = 1,
Inactive = 2
}
11-2. enumに文字列を直接設定できますか?
C#のenumに文字列を直接設定することはできません。
次のような書き方はできません。
C#enum OrderStatus
{
Pending = "未処理" // エラー
}
enumの基になる型は整数型なので、値として設定できるのは数値です。
日本語の表示名を持たせたい場合は、switch式で変換する方法があります。
C#static string ToDisplayName(OrderStatus status)
{
return status switch
{
OrderStatus.Pending => "未処理",
OrderStatus.Processing => "処理中",
OrderStatus.Shipped => "発送済み",
OrderStatus.Cancelled => "キャンセル済み",
_ => "不明"
};
}
また、Display属性などを使って表示名を付ける方法もあります。
11-3. enumとconstの違いは何ですか?
constは単一の定数を定義するために使います。
C#const int Pending = 1;
const int Processing = 2;
const int Shipped = 3;
一方、enumは関連する定数を1つの型としてまとめるために使います。
C#enum OrderStatus
{
Pending = 1,
Processing = 2,
Shipped = 3
}
constだけで管理すると、値同士の関連性が弱くなります。
C#int status = Shipped;
enumなら、注文ステータスであることが型として明確になります。
C#OrderStatus status = OrderStatus.Shipped;
関連する選択肢をまとめたい場合は、constよりenumの方が適しています。
11-4. enumの値を後から追加しても問題ありませんか?
enumの値を後から追加することはできます。
C#enum OrderStatus
{
None = 0,
Pending = 1,
Processing = 2,
Shipped = 3,
Cancelled = 4,
Returned = 5
}
ただし、追加による影響を確認する必要があります。
特に、次の箇所に注意しましょう。
switch文で新しい値に対応しているか表示名変換メソッドに追加しているか
DBやAPIで値を保存・公開していないか
JSON変換に影響がないか
既存のテストが新しい値を考慮しているか
値を追加するだけなら比較的安全ですが、既存の数値を変更するのは危険です。
DBやAPIで使っているenumの数値は、基本的に変更しないようにしましょう。
11-5. Flags属性はいつ使うべきですか?
Flags属性は、複数の値を同時に持たせたい場合に使います。
たとえば、権限管理です。
C#[Flags]
enum Permission
{
None = 0,
Read = 1,
Write = 2,
Delete = 4
}
次のように複数の値を組み合わせられます。
C#Permission permission = Permission.Read | Permission.Write;
一方、注文ステータスのように、常にどれか1つだけを表すものにはFlagsは不要です。
C#enum OrderStatus
{
Pending,
Processing,
Shipped,
Cancelled
}
Flagsを使うべきか迷った場合は、「複数選択できる値かどうか」を基準に考えると分かりやすいです。
複数選択できるならFlags、1つだけなら通常のenumを使いましょう。
まとめ
C#のenum、つまり列挙型は、決まった選択肢を分かりやすく安全に扱うための機能です。
文字列や数値だけで状態を管理すると、意味が分かりにくくなったり、不正な値が入りやすくなったりします。
enumを使うことで、次のようなメリットがあります。
値の意味が名前で分かる
コード補完が効きやすい
switch文で分岐処理を書きやすいマジックナンバーを避けられる
状態や種類を型として表現できる
一方で、C# enumには注意点もあります。
デフォルト値は0なので、None = 0やUnknown = 0を定義しておくと安全です。
また、数値からenumへキャストすると、定義されていない値でも入ってしまうため、外部入力を扱う場合はEnum.IsDefinedなどで検証することが大切です。
文字列変換では、Enum.ParseよりもEnum.TryParseを使うと安全です。
複数の値を組み合わせたい場合は、Flags属性を使い、値を1, 2, 4, 8のように2の累乗で定義します。
C# enumは、初心者が最初に覚えておきたい重要な機能のひとつです。
状態、種類、権限、カテゴリ、処理結果などを扱うときは、数値や文字列で直接管理するのではなく、enumで表現できないかを考えてみましょう。

