35

最近、次の(サンプル)列挙が...

enum Color
{
    Red,
    Green,
    Yellow,
    Blue
}

...一見よりタイプセーフなクラスに置き換えることができます:

class Color
{
    private Color() { }

    public static readonly Color   Red      = new Color();
    public static readonly Color   Green    = new Color();
    public static readonly Color   Yellow   = new Color();
    public static readonly Color   Blue     = new Color();
}

「タイプセーフ」とColorは、列挙型の場合は次のステートメントが機能しますColorが、上記のクラスの場合は機能しないことを意味します。

var nonsenseColor = (Color)17;    // works if Color is an enum

2つの質問:

1)このパターンには広く受け入れられている名前がありますか(列挙型をタイプセーフクラスに置き換えます)?

2)どのような場合に列挙型を使用する必要がありますか?また、クラスがより適切なのはいつですか?

4

6 に答える 6

30

列挙型は、軽量の状態情報に最適です。たとえば、色の列挙型 (青を除く) は、信号機の状態を照会するのに適しています。色の概念全体とそのすべての手荷物 (アルファ、色空間など) を含む真の色は重要ではありません。ライトがどの状態にあるかだけです。また、列挙型を少し変更して、信号機の状態を表します。 :

[Flags()]
public enum LightColors
{
    unknown = 0,
    red = 1,
    yellow = 2,
    green = 4,
    green_arrow = 8
}

現在のライトの状態は次のように設定できます。

LightColors c = LightColors.red | LightColors.green_arrow;

そして次のようにクエリされます:

if ((c & LightColors.red) == LightColors.red)
{
    //Don't drive
}
else if ((c & LightColors.green_arrow) == LightColors.green_arrow)
{
    //Turn
}

静的クラスの色のメンバーは、追加機能なしでこの複数の状態をサポートできます。

ただし、静的クラス メンバーは、一般的に使用されるオブジェクトには最適です。メンバーは、System.Drawing.Colorあいまいなコンストラクターを持つ既知の名前の色を表すため、優れた例です (16 進数の色を知っている場合を除きます)。それらが列挙型として実装されている場合、値を色として使用するたびに、次のようにする必要があります。

colors c = colors.red;
switch (c)
{
    case colors.red:
        return System.Drawing.Color.FromArgb(255, 0, 0);
        break;
    case colors.green:
        return System.Drawing.Color.FromArgb(0,255,0);
        break;
}

したがって、列挙型を取得していて、オブジェクトを派生させるために switch/case/if/else/whatever を常に実行していることがわかった場合は、静的クラス メンバーを使用することをお勧めします。何かの状態のみを照会している場合は、列挙型に固執します。また、安全でない方法でデータを渡す必要がある場合は、オブジェクトのシリアル化されたバージョンよりも列挙型の方がおそらくうまく機能します。

編集: @stakx、@Antonの投稿に応えて、あなたも何か重要なことに出くわしたと思います.

消費者の観点からは、System.Drawing.Color 静的クラス メンバーをすべて記述する必要があるよりも、はるかに好ましいと思います。しかし、プロデューサーの立場からすると、それをすべて書かなければならないのは大変なことです。したがって、他の人があなたのコードを使用する場合、書き込み/テスト/デバッグに 10 倍の時間がかかる場合でも、静的クラス メンバーを使用することで多くの問題を解決できる可能性があります。ただし、単に列挙型を使用し、必要に応じてキャスト/変換する方が簡単な場合があります。

于 2010-01-22T19:58:57.387 に答える
3

他の誰かが興味を持っているなら、その間に私が見つけたいくつかのこと:

  • switchブロックはenum-as-classでは機能しません。

  • ユーザーempiは、上記のクラスとしての列挙型サンプルがJava列挙型と類似していることに言及しました。Javaの世界では、 TypesafeEnumと呼ばれる認識されたパターンがあるようです。どうやらこのパターンは、ジョシュア・ブロックの著書「 EffectiveJava」にまでさかのぼります。

于 2010-01-22T19:41:29.677 に答える
3

私は実際に仕事でこのようなことでかなり苦労してきました。

これは、Jon Skeet のブログ記事「Enhanced enums in C#」でJohn B によって投稿された例に基づいています。

多くの醜さなしに適切に動作しなかったのは、基本列挙クラスを派生させ、派生型の追加の静的フィールドを追加することによって列挙を拡張することでした。

そのブログに投稿されている基本を確認すると、派生クラスが深刻な型付けの問題を持つ共通の値のセットを共有し、特定の列挙型基本クラスが他の 2 つの異なるクラスで派生すると、それらの値セットが含まれることに気付くでしょう。同じコレクションも。

つまり、DerivedEnumClass1 の変数を作成するのに苦労し、実際にはその変数に BaseEnumClass1 型である列挙コレクションから値を保存する必要があるため、回避策はありません。

もう 1 つの問題は、私たちがよく使用する Xml シリアライゼーションです。ビジネス オブジェクトの列挙変数のデータ型として 2 つのジェネリック クラスを使用することで、この問題を回避しました。それらは、それらが表すプロパティに格納された実際の値の xml シリアル化と逆シリアル化を処理しました。この方法でビットフラグの動作を再現するために必要だったのは、いくつかの演算子のオーバーロードだけでした。

これらは、私が遭遇した主要な問題の一部です。

全体として、私は最終結果にあまり満足していません。そこにはいくつかの臭いがありますが、今のところ仕事をしています.

私たちのチーフ アーキテクトがそれを機能させようと決心した理由は、実際のクラスを持ち、ジェネリックまたは特定のあらゆる種類の動作でそれらを拡張する可能性があるからです。これまでのところ、拡張メソッドで提供できなかったものはあまり見たことがありませんが、何かに遭遇する可能性があります。

ここにはコードが何もありません。週末はそれにアクセスできません。そうでなければ、どこに行ったのかを示すことができたはずです。あまりきれいではありませんが... :o

于 2010-01-22T19:47:14.440 に答える
1

一緒に論理和演算される可能性のあるあいまいなビット フラグ値が必要なレガシー コードと相互運用する必要がある場合は、クラス定数を使用します。この場合、列挙型は次のようになります

public enum Legacy : ushort
{
  SomeFlag1 = 0x0001,
  SomeFlag2 = 0x0002,
  // etc...
}

次に、列挙型を適切な値に変換するためにキャストが必要なため、P/Invoke でのマーシャリングは読みにくくなります。

const クラス変数の場合、キャストは必要ありません。

于 2010-01-22T19:27:02.410 に答える
1

どちらのアプローチも有効です。ケースごとに選択する必要があります。

列挙型がビット操作をフラグとしてサポートしていることを追加できます (このセマンティクスをサポートし、列挙型からきれいな文字列を生成するための [Flags] 属性さえあります)。

Replaceing Enums with the Strategy Pattern という非常によく似たリファクタリングがあります。あなたの場合、色は受動的であり、戦略のようには機能しないため、完全ではありません。しかし、それを特別なケースと考えてみませんか?

于 2010-01-22T20:08:25.003 に答える
0

うん、Java 1.5 より前にリリースされ、Java の列挙型がネイティブにサポートされた Joshua Bloch による「Effective Java」のオリジナル版は、Typesafe Enumパターンを示していました。残念ながら、本の最新リリースは Java > 1.5 を対象としているため、代わりに Java 列挙型を使用しています。

次に、Java では int から enum にキャストできません。

Color nonsenseColor = (Color)17; // compile-error

しかし、私は他の言語について話すことはできません。

于 2010-01-22T19:58:24.297 に答える