Erweitern einer Aufzählung durch Vererbung

83

Ich weiß, dass dies eher gegen die Idee von Aufzählungen verstößt, aber ist es möglich, Aufzählungen in C # / Java zu erweitern? Ich meine "erweitern" sowohl im Sinne des Hinzufügens neuer Werte zu einer Aufzählung als auch im Sinne des Erbens von einer vorhandenen Aufzählung.

Ich gehe davon aus, dass dies in Java nicht möglich ist, da es sie erst vor relativ kurzer Zeit erhalten hat (Java 5?). C # scheint jedoch eher verzeihend für Leute zu sein, die verrückte Dinge tun wollen, also dachte ich, dass es irgendwie möglich sein könnte. Vermutlich könnte es durch Reflexion gehackt werden (nicht, dass Sie alle diese Methode tatsächlich anwenden würden)?

Ich bin nicht unbedingt daran interessiert, eine bestimmte Methode zu implementieren, sie hat nur meine Neugier geweckt, als sie mir einfiel :-)

Alastairs
quelle
Beantwortet das deine Frage? Enum "Inheritance"
T.Todua

Antworten:

103

Der Grund, warum Sie Enums nicht erweitern können, liegt darin, dass dies zu Problemen mit dem Polymorphismus führen würde.

Angenommen, Sie haben eine Aufzählung MyEnum mit den Werten A, B und C und erweitern sie mit dem Wert D als MyExtEnum.

Angenommen, eine Methode erwartet irgendwo einen myEnum-Wert, beispielsweise als Parameter. Es sollte legal sein, einen MyExtEnum-Wert anzugeben, da es sich um einen Subtyp handelt. Was werden Sie nun tun, wenn sich herausstellt, dass der Wert D ist?

Um dieses Problem zu beheben, ist das Erweitern von Aufzählungen illegal

Rik
quelle
4
Eigentlich ist der Grund einfach, dass es keinen Sinn ergibt. Das von Ihnen erwähnte Problem, dh Client-Code, der eine Konstante empfängt, die er nicht erwartet, besteht bei der aktuellen Implementierung weiterhin. Tatsächlich lässt der Compiler Sie keine switchEnum-Werte zu, ohne einen defaultFall anzugeben oder eine Ausnahme auszulösen. Und selbst wenn dies der Fall wäre, könnte die Aufzählung zur Laufzeit aus einer separaten Zusammenstellung stammen
Raffaele
@ Raffaele Ich habe es gerade versucht, und zumindest in Java 7 können Sie eine Aufzählung ohne defaultFall oder Werfen einschalten .
Dathan
@Dathan du hast definitiv recht! Wie es geschrieben steht, ist das einfach falsch - vielleicht sollte ich es entfernen? Ich habe an den Fall gedacht, als Sie a verwenden switch, um einen erforderlichen Wert anzugeben, z. B. die Initiale eines lokalen oder anreturn
Raffaele
3
Das ist nicht persönlich, Rik. Aber das ist so ziemlich der schlimmste Grund überhaupt! Polymorphismus und Aufzählung ...DayOfWeek a = (DayOfWeek) 1; DayOfWeek b = (DayOfWeek) 4711; Console.WriteLine(a + ", " + b);
Bitterblue
41

Wenn eingebaute Aufzählungen nicht ausreichen, können Sie dies auf die altmodische Weise tun und Ihre eigenen erstellen. Wenn Sie beispielsweise eine zusätzliche Eigenschaft hinzufügen möchten, z. B. ein Beschreibungsfeld, können Sie dies wie folgt tun:

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");
}

Sie können es dann wie eine Aufzählung behandeln:

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

Der Trick besteht darin, sicherzustellen, dass der Konstruktor privat ist (oder geschützt ist, wenn Sie erben möchten) und dass Ihre Instanzen statisch sind.

Tsimon
quelle
Ich bin keine C # -Person, aber willst du nicht ein bisschen endgültig (oder versiegelt / const / was auch immer)?
Tom Hawtin - Tackline
1
Ich glaube nicht. Oder zumindest nicht für die Situation, in der Sie von dieser Klasse erben möchten, um neue "Enum" -Werte hinzuzufügen. Endgültig und versiegelt verhindern Vererbung IIRC.
Alastairs
4
@alastairs Ich denke, er meinte eher das Hinzufügen finalzur public static Action DoIt = new Action("Do it", "This does things");Zeile als zur Klasse.
Crush
1
Wie durch Hinzufügen readonly, ala:public static readonly Action DoIt = new Action("Do it", "This does things");
ErikE
40

Sie gehen den falschen Weg: Eine Unterklasse einer Aufzählung hätte weniger Einträge.

Denken Sie im Pseudocode:

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

Jede Methode, die ein Tier aufnehmen kann, sollte ein Säugetier aufnehmen können, aber nicht umgekehrt. Unterklassen dienen dazu, etwas spezifischer und nicht allgemeiner zu machen. Deshalb ist "Objekt" die Wurzel der Klassenhierarchie. Wenn Aufzählungen vererbbar wären, hätte eine hypothetische Wurzel der Aufzählungshierarchie jedes mögliche Symbol.

Aber nein, C # / Java erlaubt keine Unteraufzählungen, AFAICT, obwohl es manchmal wirklich nützlich wäre. Dies liegt wahrscheinlich daran, dass Enums als Ints (wie C) anstelle von internen Symbolen (wie Lisp) implementiert wurden. (Was repräsentiert (Tier) 1 oben und was repräsentiert (Säugetier) 1 und haben sie den gleichen Wert?)

Sie könnten jedoch Ihre eigene enumähnliche Klasse (mit einem anderen Namen) schreiben, die dies vorsieht. Mit C # -Attributen sieht es vielleicht sogar gut aus.

Ken
quelle
3
interessante Analyse. Ich habe es nie so gesehen!
Nerrve
Interessant, aber ich denke es ist falsch. Zu implizieren, dass es weniger Typen in einer Unterklasse geben sollte, ist in Bezug auf Tierklassifizierungsbäume sinnvoll, jedoch nicht in Bezug auf Code. Wenn Sie im Code eine Unterklasse erstellen, gibt es nie weniger Methoden. Es gibt nie weniger Mitgliedsvariablen. Ihr Argument scheint nicht für Software zu gelten, sondern für Tierklassifikationen.
Kieveli
4
@ Kieveli, deine Analyse ist falsch. Das Hinzufügen eines Elements hat nicht die gleiche Auswirkung auf ein Objekt wie das Hinzufügen zu den möglichen Werten. Das hat Ken nicht erfunden; Es gibt klare und präzise Regeln der Informatik, die erklären, warum dies so funktioniert. Versuchen Sie, nach Kovarianz und Kontravarianz zu suchen (nicht nur nach akademischer Masturbation, sondern auch nach Regeln für Funktionssignaturen und Container).
JWG
@Kieveli Angenommen, Sie hatten eine 'StreamWriter'-Klasse (nimmt Stream - schreibt Stream). Sie würden 'TextWriter: StreamWriter' erstellen (nimmt stream - schreibt Text). Jetzt erstellen Sie 'HtmlWriter: TextWriter' (nimmt Stream - schreibt hübsches HTML). Jetzt hat der HtmlWriter offensichtlich mehr Mitglieder, Methoden usw. Versuchen Sie nun, einen Stream von einer Soundkarte zu nehmen und ihn mit HtmlWriter in eine Datei zu schreiben :)
evictednoise
Dieses Beispiel ist erfunden und beweist nicht, dass es in der erweiterten Klasse niemals MEHR Enum-Werte geben sollte. enum VehicalParts {Lizenz, Geschwindigkeit, Kapazität}; enum CarParts {SteeringWheel, Winshield}; + alles, was Vehical auch hat
Bernoulli Lizard
12

Aufzählungen sollen die Aufzählung aller möglichen Werte darstellen, daher widerspricht das Erweitern eher der Idee.

In Java (und vermutlich in C ++ 0x) können Sie jedoch eine Schnittstelle anstelle einer Enum-Klasse verwenden. Fügen Sie dann Standardwerte in eine Aufzählung ein, die die Funktion implementiert. Offensichtlich können Sie java.util.EnumSet und dergleichen nicht verwenden. Dies ist der Ansatz, der in "mehr NIO-Funktionen" verfolgt wird, die in JDK7 enthalten sein sollten.

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


public enum WTFResults implements Result {
    FILE_NOT_FOUND
}
Tom Hawtin - Tackline
quelle
4

Sie können .NET Reflection verwenden, um die Beschriftungen und Werte zur Laufzeit aus einer vorhandenen Enumeration abzurufen ( Enum.GetNames()und Enum.GetValues()sind die beiden spezifischen Methoden, die Sie verwenden würden), und dann mithilfe der Code-Injection eine neue mit diesen und einigen neuen Elementen erstellen. Dies scheint etwas analog zu "Erben von einer bestehenden Aufzählung".

McKenzieG1
quelle
2

Das Hinzufügen von Aufzählungen ist ziemlich häufig, wenn Sie zum Quellcode zurückkehren und ihn bearbeiten. Eine andere Methode (Vererbung oder Reflexion, falls möglich) wird Sie wahrscheinlich wieder treffen, wenn Sie ein Upgrade der Bibliothek und erhalten Sie haben den gleichen Aufzählungsnamen oder den gleichen Aufzählungswert eingeführt. Ich habe viel Code auf niedriger Ebene gesehen, bei dem die Ganzzahl mit der Binärcodierung übereinstimmt, bei der Probleme auftreten würden

Im Idealfall sollten Code-Referenzierungs-Enums nur als gleich (oder als Schalter) geschrieben werden. Versuchen Sie, zukunftssicher zu sein, indem Sie nicht erwarten, dass der Enum-Satz const ist

Oskar
quelle
2

Wenn Sie meinen, erstreckt sich im Sinne der Basisklasse, dann in Java ... nein.

Sie können einen Aufzählungswert jedoch um Eigenschaften und Methoden erweitern, wenn Sie dies meinen.

Im Folgenden wird beispielsweise eine Bracket-Aufzählung verwendet:

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;
    }        
}
Allain Lalonde
quelle
Dies klingt nach einem Werttyp (Struktur) in .NET. Ich wusste nicht, dass Sie dies in Java tun können.
Alastairs
2

Ich habe niemanden gesehen, der dies erwähnte, aber der Ordnungswert einer Aufzählung ist wichtig. Bei Grails wird beispielsweise beim Speichern einer Aufzählung in der Datenbank der Ordnungswert verwendet. Wenn Sie eine Aufzählung irgendwie erweitern könnten, was wären die Ordnungswerte Ihrer Erweiterungen? Wenn Sie es an mehreren Stellen erweitern würden, wie könnten Sie eine Art Ordnung für diese Ordnungszahlen bewahren? Chaos / Instabilität in den Ordnungswerten wäre eine schlechte Sache, was wahrscheinlich ein weiterer Grund ist, warum die Sprachdesigner dies nicht berührt haben.

Eine weitere Schwierigkeit, wenn Sie der Sprachdesigner waren, wie können Sie die Funktionalität der values ​​() -Methode beibehalten, die alle Enum-Werte zurückgeben soll. Worauf würden Sie sich berufen und wie würden alle Werte erfasst?

Darrell Denlinger
quelle
0

Hmmm - soweit ich weiß, ist dies nicht möglich - Aufzählungen werden zur Entwurfszeit geschrieben und dienen dem Programmierer als Annehmlichkeit.

Ich bin mir ziemlich sicher, dass beim Kompilieren des Codes die Namen in Ihrer Aufzählung durch die entsprechenden Werte ersetzt werden, wodurch das Konzept einer Aufzählung und (daher) die Möglichkeit, sie zu erweitern, entfernt werden.

Chris Roberts
quelle
0

Ich möchte in der Lage sein, C # -Aufzählungen, die Kombinationen vorhandener Werte sind, Werte hinzuzufügen. Zum Beispiel (das ist, was ich tun möchte):

AnchorStyles ist definiert als

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

und ich möchte einen AnchorStyles.BottomRight = Right + Bottom hinzufügen, anstatt zu sagen

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

Ich kann nur sagen

my_ctrl.Anchor = AnchorStyles.BottomRight;

Dies verursacht keine der oben genannten Probleme, daher wäre es schön, wenn es möglich wäre.

Magnum
quelle
0

Vor einiger Zeit wollte sogar ich so etwas machen und stellte fest, dass Enum-Erweiterungen viele grundlegende Konzepte zunichte machen würden ... (Nicht nur Polymorphisim)

Möglicherweise müssen Sie dies dennoch tun, wenn die Aufzählung in einer externen Bibliothek deklariert ist. Denken Sie daran, dass Sie bei der Verwendung dieser Aufzählungserweiterungen besondere Vorsicht walten lassen sollten ...

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;
   }
}
Sultan
quelle
0

In Bezug auf Java ist dies nicht zulässig, da das Hinzufügen von Elementen zu einer Aufzählung effektiv eine Superklasse anstelle einer Unterklasse erstellen würde.

Erwägen:

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

Ein allgemeiner Anwendungsfall des Polymorphismus wäre

 Person person = Student.ROSS;   //not legal

das ist eindeutig falsch.

Aniket Thakur
quelle
0

Eine vorübergehende / lokale Problemumgehung , wenn Sie nur eine sehr lokale / einmalige Verwendung wünschen:

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   );

Alle Antworten finden Sie unter: Enum "Inheritance"

T.Todua
quelle