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には、GuestMemberAdminという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を使う方法

enumswitch文との相性が非常に良いです。

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

最初の値であるPending0なので、defaultの結果はOrderStatus.Pendingになります。

C# enumを設計するときは、この「デフォルト値が0」という仕様を意識することが大切です。

3-2. 明示的に数値を割り当てる方法

enumの各値には、明示的に数値を割り当てることもできます。

C#
enum OrderStatus
{
Pending = 1,
Processing = 2,
Shipped = 3,
Cancelled = 4
}

このように書くと、Pending1Processing2になります。

数値に意味がある場合や、外部システム・データベース・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になります。

これは分かりにくく、バグの原因になることがあります。

そのため、次のようにNoneUnknown0として定義しておくと安全です。

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
}

一方、大きな値やビットフラグを扱う場合は、longulongを使うこともあります。

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

この例では、3OrderStatus.Shippedに対応しているため、Shippedと表示されます。

ただし、ここで重要なのは、定義されていない数値でもenumにキャストできてしまうという点です。

C#
int value = 99;

OrderStatus status = (OrderStatus)value;

Console.WriteLine(status); // 99

OrderStatus99という値を定義していなくても、キャスト自体は成功します。

そのため、外部から受け取った数値を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("不正な値です");
}

valueOrderStatusに定義されている値であれば、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.ParseEnum.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

ReadWriteを組み合わせると、次のようになります。

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を定義する方が分かりやすい場合もあります。

nullNoneのどちらを使うかは、意味を分けて考えることが大切です。

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だけでは何の状態なのか分かりにくく、ABも意味が不明です。

より具体的に、何を表す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 = 0Unknown = 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 = 0Unknown = 0を定義しておくと安全です。

また、数値からenumへキャストすると、定義されていない値でも入ってしまうため、外部入力を扱う場合はEnum.IsDefinedなどで検証することが大切です。

文字列変換では、Enum.ParseよりもEnum.TryParseを使うと安全です。

複数の値を組み合わせたい場合は、Flags属性を使い、値を1, 2, 4, 8のように2の累乗で定義します。

C# enumは、初心者が最初に覚えておきたい重要な機能のひとつです。

状態、種類、権限、カテゴリ、処理結果などを扱うときは、数値や文字列で直接管理するのではなく、enumで表現できないかを考えてみましょう。