継承による列挙型の拡張


85

これは列挙型の概念に反することを私は知っていますが、C#/ Javaで列挙型を拡張することは可能ですか?私は、列挙型に新しい値を追加するという意味だけでなく、既存の列挙型から継承するというオブジェクト指向の意味でも「拡張する」ことを意味します。

Javaではごく最近(Java 5?)しか入手できなかったので、それは不可能だと思います。C#は、クレイジーなことをしたい人にはもっと寛容なようですが、それは何らかの方法で可能かもしれないと思いました。おそらくそれはリフレクションを介してハッキングされる可能性があります(実際にその方法を使用するわけではありません)?

私は特定のメソッドを実装することに必ずしも興味はありません、それが私に起こったときそれは私の好奇心を引き起こしただけです:-)


これはあなたの質問に答えますか?列挙型「継承」
T.Todua

回答:


103

列挙型を拡張できない理由は、ポリモーフィズムの問題につながるためです。

値A、B、およびCを持つ列挙型MyEnumがあり、それを値DでMyExtEnumとして拡張するとします。

メソッドがmyEnum値をどこか、たとえばパラメーターとして期待しているとします。MyExtEnum値はサブタイプであるため、指定することは合法ですが、値がDであることが判明した場合はどうしますか?

この問題を排除するために、列挙型を拡張することは違法です


4
実際、その理由は単に意味がないからです。あなたが言及する問題、つまりクライアントコードが予期しない定数を受け取ることは、現在の実装でもまだ存在します-実際、コンパイラはswitchdefaultケースを提供するか例外をスローすることなく、列挙値を許可しません。そして、たとえそうだったとしても、実行時の列挙型は別のコンパイルから来る可能性があります
Raffaele 2014年

@Raffaele私はこれを試しましたが、少なくともJava 7では、defaultケースやスローなしで列挙型をオンに切り替えることができます。
デイサン2014年

@Dathanあなたは間違いなく正しいです!それが書かれているように、それは単に間違っています-多分私はそれを削除する必要がありますか?switchローカルのイニシャルなど、必要な値を提供するためにaを使用する場合、またはreturn
Raffaele 2014年

3
これは個人的なことではありません、リック。しかし、これはこれまでで最悪の理由です!多型と列挙型...DayOfWeek a = (DayOfWeek) 1; DayOfWeek b = (DayOfWeek) 4711; Console.WriteLine(a + ", " + b);
Bitterblue

41

組み込みの列挙型では不十分な場合は、昔ながらの方法で独自に作成できます。たとえば、説明フィールドなどのプロパティを追加する場合は、次のように実行できます。

public class Action {
    public string Name {get; private set;}
    public string Description {get; private set;}

    private Action(string name, string description) {
        Name = name;
        Description = description;
    }

    public static Action DoIt = new Action("Do it", "This does things");
    public static Action StopIt = new Action("Stop It", "This stops things");
}

次に、次のように列挙型のように扱うことができます。

public void ProcessAction(Action a) {
    Console.WriteLine("Performing action: " + a.Name)
    if (a == Action.DoIt) {
       // ... and so on
    }
}

秘訣は、コンストラクターがプライベート(または継承する場合は保護されている)であり、インスタンスが静的であることを確認することです。


私はC#の人ではありませんが、そこに少しファイナル(または封印/定数/その他)を入れたくないですか?
トムホーティン-タックライン2008

1
私はそうは思わない。または、少なくとも、このクラスから継承して新しい「列挙型」値を追加したい状況ではありません。最終的で封印されたものは継承IIRCを防ぎます。
alastairs 2008

4
@alastairs彼はクラスではなくfinalpublic static Action DoIt = new Action("Do it", "This does things");行に追加することを意味したと思います。
ときめき

1
を追加するreadonly場合と同様に、ala:public static readonly Action DoIt = new Action("Do it", "This does things");
ErikE 2015

40

あなたは間違った方向に進んでいます:列挙型のサブクラスはより少ないエントリを持っているでしょう。

擬似コードでは、次のように考えます。

enum Animal { Mosquito, Dog, Cat };
enum Mammal : Animal { Dog, Cat };  // (not valid C#)

動物を受け入れることができるすべての方法は、哺乳類を受け入れることができるはずですが、その逆はできません。サブクラス化は、より一般的なものではなく、より具体的なものを作成するためのものです。そのため、「オブジェクト」はクラス階層のルートです。同様に、列挙型が継承可能である場合、列挙型階層の仮想ルートにはすべての可能なシンボルがあります。

ただし、C#/ Javaではサブ列挙型のAFAICTは許可されていませんが、実際に役立つ場合もあります。これはおそらく、列挙型をインターンシンボル(Lispなど)ではなくint(Cなど)として実装することを選択したためです。(上記では、(Animal)1は何を表し、(Mammal)1は何を表しますか?それらは同じ値ですか?)

ただし、これを提供する独自の列挙型のようなクラス(別の名前)を作成することもできます。C#属性を使用すると、見栄えがよくなる場合もあります。


3
興味深い分析。このように考えたことはありません!
Nerrve 2012

興味深いですが、私はそれが間違っていると思います。サブクラス内のタイプが少ないことを意味することは、動物分類ツリーの観点からは意味がありますが、コードの観点からは意味がありません。コードをサブクラス化する場合、メソッドが少なくなることはありません。メンバー変数が少なくなることはありません。あなたの議論は、動物の分類のようにソフトウェアには当てはまらないようです。
Kieveli 2013年

4
@Kieveli、あなたの分析は間違っています。メンバーを追加しても、可能な値のセットに追加するのと同じ効果はオブジェクトにありません。これはケンが作り上げたものではありません。コンピュータサイエンスには、なぜこのように機能するのかを説明する明確で正確なルールがあります。共分散と反変性を検索してみてください(アカデミックマスターベーションだけでなく、関数のシグネチャやコンテナーなどのルールを理解するのに役立ちます)。
jwg 2013年

@ Kieveli'StreamWriter 'クラス(ストリームを取得-ストリームを書き込み)があるとします。'TextWriter:StreamWriter'を作成します(ストリームを取得し、テキストを書き込みます)。ここで、「HtmlWriter:TextWriter」を作成します(ストリームを取得し、かなりのhtmlを書き込みます)。現在、HtmlWriterには明らかに、より多くのメンバー、メソッドなどがあります。次に、サウンドカードからストリームを取得し、HtmlWriterを使用してファイルに書き込んでみます:)
evictednoise 2016

この例は考案されたものであり、拡張クラスにこれ以上列挙値が存在してはならないことを証明するものではありません。列挙型VehicalParts {ライセンス、速度、容量}; 列挙型CarParts {SteeringWheel、Winshield}; + Vehicalが持っているすべてのもの
Bernoulli Lizard

12

列挙型はすべての可能な値の列挙型を表すことになっているので、拡張することはむしろ考えに反します。

ただし、Java(およびおそらくC ++ 0x)で実行できるのは、列挙型クラスの代わりにインターフェイスを使用することです。次に、機能を実装する列挙型に標準値を入力します。もちろん、java.util.EnumSetなどを使用することはできません。これは、JDK7にあるはずの「その他のNIO機能」で採用されているアプローチです。

public interface Result {
    String name();
    String toString();
}
public enum StandardResults implements Result {
    TRUE, FALSE
}


public enum WTFResults implements Result {
    FILE_NOT_FOUND
}

4

あなたは、実行時に既存の列挙型のラベルと値を取得するには、.NETリフレクションを使用(することができますEnum.GetNames()し、Enum.GetValues()あなたが使用する2つの特定の方法です)、その後、これらの要素に加えて、いくつかの新しいものと新しいものを作成するために、コードインジェクションを使用しています。これは、「既存の列挙型から継承する」こととやや類似しているようです。


2

列挙型を追加することは、ソースコードに戻って編集する場合に行うのがかなり一般的なことです。他の方法(可能な場合は継承または反映)が戻ってきて、ライブラリのアップグレードを取得したときにヒットする可能性があります。彼らは同じ列挙型名または同じ列挙型値を導入しました-整数がバイナリエンコーディングと一致する低レベルのコードをたくさん見ましたが、そこで問題が発生します

理想的には、列挙型を参照するコードは、equalsのみ(またはスイッチ)として記述し、列挙型セットがconstであると期待しないことで将来の証拠となるようにしてください


2

基本クラスの意味で拡張するという意味であれば、Javaでは...いいえ。

ただし、列挙値を拡張して、プロパティとメソッドを含めることができます。

たとえば、次の例ではブラケット列挙型を使用しています。

class Person {
    enum Bracket {
        Low(0, 12000),
        Middle(12000, 60000),
        Upper(60000, 100000);

        private final int low;
        private final int high;
        Brackets(int low, int high) {
            this.low = low;
            this.high = high;
        }

        public int getLow() {
            return low;
        }

        public int getHigh() {
            return high;
        }

        public boolean isWithin(int value) {
           return value >= low && value <= high;
        }

        public String toString() {
            return "Bracket " + low + " to " + high;
        }
    }

    private Bracket bracket;
    private String name;

    public Person(String name, Bracket bracket) {
        this.bracket = bracket;
        this.name = name;
    }

    public String toString() {
        return name + " in " + bracket;
    }        
}

これは、.NETの値型(構造体)のように聞こえます。Javaでこれができるとは知りませんでした。
alastairs 2011

2

他の誰もこれについて言及しているのを見ませんでしたが、列挙型の序数の値は重要です。たとえば、Grailsを使用して列挙型をデータベースに保存すると、序数が使用されます。どういうわけか列挙型を拡張できるとしたら、拡張の序数は何でしょうか?複数の場所に拡張した場合、これらの序数に何らかの順序を維持するにはどうすればよいでしょうか。序数のカオス/不安定性は悪いことであり、おそらく言語設計者がこれに触れていないもう1つの理由です。

言語設計者の場合のもう1つの問題は、すべての列挙値を返すことになっているvalues()メソッドの機能をどのように保持できるかということです。これを何で呼び出し、どのようにすべての値を収集しますか?




0

うーん-私が知る限り、これはできません-列挙は設計時に書かれ、プログラマーの便宜のために使用されます。

コードがコンパイルされると、列挙型の名前が同等の値に置き換えられると確信しています。これにより、列挙型の概念が削除され、(したがって)列挙型を拡張することができなくなります。


0

既存の値の組み合わせであるC#列挙に値を追加できるようにしたいと思います。たとえば(これが私がやりたいことです):

AnchorStylesは次のように定義されます

public enum AnchorStyles { None = 0, Top = 1, Bottom = 2, Left = 4, Right = 8, }

そして、私はAnchorStyles.BottomRight = Right + Bottomを追加したいので、言う代わりに

my_ctrl.Anchor = AnchorStyles.Right | AnchorStyles.Bottom;

私はただ言うことができます

my_ctrl.Anchor = AnchorStyles.BottomRight;

これは上記の問題を引き起こさないので、可能であればいいのですが。


0

しばらく前に、私でさえこのようなことをしたいと思っていて、列挙型拡張が多くの基本的な概念を台無しにすることに気づきました...(多形性だけでなく)

ただし、列挙型が外部ライブラリで宣言されている場合は、それでも実行する必要があります。この列挙型拡張機能を使用する場合は、特別な注意が必要です。

public enum MyEnum { A = 1, B = 2, C = 4 }

public const MyEnum D = (MyEnum)(8);
public const MyEnum E = (MyEnum)(16);

func1{
    MyEnum EnumValue = D;

    switch (EnumValue){
      case D:  break;
      case E:  break;
      case MyEnum.A:  break;
      case MyEnum.B:  break;
   }
}

0

Javaに関する限り、列挙型に要素を追加すると、サブクラスではなくスーパークラスが効果的に作成されるため、許可されていません。

考えてみましょう:

 enum Person (JOHN SAM}   
 enum Student extends Person {HARVEY ROSS}

ポリモーフィズムの一般的な使用例は次のとおりです。

 Person person = Student.ROSS;   //not legal

これは明らかに間違っています。


0

非常にローカル/ 1回限りの使用が必要な場合の、一時的/ローカルな回避策

enum Animals { Dog, Cat }
enum AnimalsExt { Dog = Animals.Dog, Cat= Animals.Cat,  MyOther}
// BUT CAST THEM when using:
var xyz = AnimalsExt.Cat;
MethodThatNeedsAnimal(   (Animals)xyz   );

すべての回答を参照してください:列挙型「継承」

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.