MS_EnumDefinition - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki

列挙型(Enum)の定義

概要

列挙型(Enum)の定義とビット演算(C# の例)

詳細

基本

値を省略

enum 列挙型名
{
  メンバー1,
  メンバー2,,
  メンバーn
}

値を明示

enum 列挙型名 : 内部的な型
{
  メンバー1 = メンバー1の値,
  メンバー2 = メンバー2の値,,
  メンバーn = メンバーnの値
}

補足(値を明示すべき場面): 値を省略すると 0 から順に採番される。
このため、

  • メンバーを途中に挿入すると、以降の値がすべてずれる
  • DB やファイルに数値で永続化していると、既存データの意味が変わる

という事故が起きる。

// 危険:Beta を足すと Released の値が 1 → 2 に変わる
enum Status { Alpha, Beta, Released }

// 安全:明示しておく
enum Status { Alpha = 1, Beta = 2, Released = 3 }

永続化する enum は必ず値を明示するのが原則。

なお、既定の基になる型は int で、
: byte : long などで変更できる。
ビット演算で 32 個を超えるフラグが要る場合は long にする。

その他

ビット演算

移行メモ([Flags] の役割): 正確には、
[Flags] が無くてもビット演算自体はできる
(enum は数値なので |& が使える)。

[Flags] が変えるのは主に次の 2 点である。

[Flags] なし [Flags] あり
ToString() 一致する名前が無ければ数値5 "Read, Write" と列挙される
HasFlag() 使える(が意味が曖昧) 意図が明確になる
読み手への意思表示 無し 「組み合わせて使う」と分かる
[Flags]
enum Permission
{
    None    = 0,        // 0 は必ず None として用意する
    Read    = 1 << 0,   // 1
    Write   = 1 << 1,   // 2
    Execute = 1 << 2,   // 4
    All     = Read | Write | Execute
}

var p = Permission.Read | Permission.Write;
bool canWrite = p.HasFlag(Permission.Write);   // true
bool canWrite2 = (p & Permission.Write) != 0;  // こちらの方が速い

要点:

  • 値は 2 の冪1 << n)にする。連番にすると重なって壊れる。
  • 0NoneHasFlag(0) は常に true になるため、判定に使わない)。
  • HasFlag はボックス化を伴うため、性能が要る場面では & を使う

ToString() 高速化(.NET の Reflection

  • Reflection だから遅いらしい。
  • メソッドを動的生成してキャッシュさせる高速化の技法がある。

補足(なぜ遅いのか / 現在の対処): Enum.ToString()
メタデータから名前を検索するため、内部でリフレクションが働く。
ループ内で大量に呼ぶと無視できないコストになる。

原文が言う「メソッドを動的生成してキャッシュ」は、
式木Reflection.Emit
switch 相当のデリゲートを作る手法である。

補足(現在の選択肢):

手段 備考
Dictionary<TEnum, string> に事前展開 最も単純で効果的
nameof 定数が決まっているなら最速(コンパイル時に解決)
[EnumMember] + ソース ジェネレーター シリアライズ用途
Enum.GetName<T>(T)(.NET 8+) ジェネリック版でボックス化を回避

近年の .NET では Enum 系 API 自体が最適化されており、
まず計測してから対処するのが妥当である。

参考

Microsoft Learn


Tags: 移行, .NET開発, ASP.NET

⚠️ **GitHub.com Fallback** ⚠️