列挙型デザイン

列挙型は、特別な種類の値の型です。 列挙型には、単純な列挙型とフラグ列挙型の 2 種類があります。

単純な列挙型は、小さな閉じられた選択肢のセットを表します。 単純な列挙型の一般的な例としては、色のセットがあります。

フラグ列挙型は、列挙値に対するビットごとの演算をサポートするように設計されています。 フラグ列挙型の一般的な例としては、演算のリストがあります。

✔️ 列挙型は、値のセットを表すパラメーター、プロパティ、戻り値を厳密に型指定するために使用してください。

✔️ 静的な定数ではなく列挙型を優先して使用してください。

❌ 開いたセット (オペレーティング システムのバージョンや友人の名前など) には列挙型を使用しないでください。

❌ 将来使用する目的で予約されている列挙値を指定しないでください。

既存の列挙型には、後の段階でいつでも値を追加できます。 列挙型への値の追加の詳細については、「列挙型への値の追加」を参照してください。 予約された値は、実際の値のセットを汚染するだけで、ユーザー エラーを引き起こす傾向があります。

❌ 1 つの値のみを持つ列挙型をパブリックに公開しないようにしてください。

C API の将来的な拡張性を確保するための一般的な方法は、予約されているパラメーターをメソッド シグネチャに追加することです。 このような予約されているパラメーターは、単一の既定値を持つ列挙型として表すことができます。 これは、マネージド API では実行しないでください。 将来のリリースでは、メソッドのオーバーロードを使用することにより、パラメーターを追加できます。

❌ 列挙型に sentinel 値を含めないでください。

sentinel 値は、フレームワーク開発者にとって有用な場合もありますが、フレームワークのユーザーの混乱を招きます。 これらは、列挙型で表されるセットの値の 1 つではなく、列挙型の状態を追跡するために使用されます。

単純な列挙型では、値 0 を指定してください。

"None" などの値を呼び出すことを検討してください。このような値が、この特定の列挙型に適していない場合、列挙型の最も一般的な既定値には、基になる値 0 を代入する必要があります。

✔️ 次のいずれかに該当する場合を除いて、列挙型の基になる型として Int32 (ほとんどのプログラミング言語での既定値) を使用することを検討してください。

  • 列挙型がフラグ列挙型であり、32 個を超えるフラグがあるか、将来さらに増えることが予想される。

  • 異なるサイズの列挙型を想定したアンマネージド コードとの相互運用性を容易にするために、基になる型は Int32 とは異なる必要がある。

  • 基になる型を小さくすると、スペースが大幅に節約される。 列挙型が主として制御フローの引数として使用されることが予想される場合、サイズによる違いはほとんどありません。 次の場合、サイズの節約が重要になります。

    • 列挙型が、非常に頻繁にインスタンス化される構造またはクラスのフィールドとして使用されることが予想される。

    • ユーザーが、列挙型インスタンスの大きな配列またはコレクションを作成することが予想される。

    • 列挙型の多数のインスタンスをシリアル化することが予想される。

インメモリを使用する場合、マネージド オブジェクトは常に DWORD 配置されることにご注意ください。したがって、インスタンスの合計サイズが常に DWORD に切り上げられるため、より小さな構造体を効果的にパックして違いを生むために、インスタンス内に複数の列挙型または他の小さな構造体を効果的に使用する必要があります。

✔️ フラグ列挙型の名前には、複数形の名詞または名詞句を使用し、単純な列挙型には、単数形の名詞または名詞句を使用してください。

System.Enum を直接拡張しないでください。

System.Enum は、ユーザー定義の列挙型を作成するために CLR によって使用される特別な型です。 ほとんどのプログラミング言語では、この機能にアクセスできるプログラミング要素が用意されています。 たとえば、C# では、enum キーワードを使用して列挙型を定義します。

フラグ列挙型のデザイン

✔️ フラグ列挙型には、System.FlagsAttribute を適用してください。 単純な列挙型には、この属性を適用しないでください。

✔️ ビットごとの OR 演算を使用して自由に組み合わせることができるように、フラグ列挙型の値には、2 の累乗を使用してください。

✔️ 一般的に使用されるフラグの組み合わせには、特別な列挙値を指定することを検討してください。

ビットごとの演算は高度な概念であり、単純なタスクには必要ありません。 ReadWrite は、このような特別な値の一例です。

❌ 値の特定の組み合わせが無効な場合、フラグ列挙型を作成しないようにしてください。

❌ 次のガイドラインで規定されているように、値が "すべてのフラグがクリアされている" ことを表し、適切な名前が付けられている場合を除いて、フラグ列挙型の値として 0 を使用しないようにしてください。

✔️ 値が 0 のフラグ列挙型には、名前 None を付けてください。 フラグ列挙型の場合、値は常に "すべてのフラグがクリアされている" ことを意味する必要があります。

列挙型への値の追加

列挙型を既に出荷した後で、列挙型に値を追加する必要があることに気付くのはよくあることです。 新しく追加された値が既存の API から返されるときに、アプリケーションの互換性の問題が発生するおそれがあります。これは、記述が不十分なアプリケーションでは、新しい値が正しく処理されないためです。

✔️ 互換性のリスクが小さい場合でも、列挙型に値を追加することを検討してください。

列挙型への追加に起因するアプリケーションの非互換性について実際のデータがある場合は、新しい値と古い値を返す新しい API を追加し、古い API を廃止することを検討してください。古い API は古い値のみを返し続けるだけです。 これにより、既存のアプリケーションは互換性を維持することが保証されます。

Portions © 2005, 2009 Microsoft Corporation. All rights reserved.

2008 年 10 月 22 日に Microsoft Windows Development シリーズの一部として、Addison-Wesley Professional によって発行された、Krzysztof Cwalina および Brad Abrams による「Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, 2nd Edition」 (フレームワーク デザイン ガイドライン: 再利用可能な .NET ライブラリの規則、用法、パターン、第 2 版) から Pearson Education, Inc. の許可を得て再印刷されています。

関連項目