Estendendo um enum via inheritance

Eu sei que isso vai contra a idéia de enums, mas é possível estender enums em C # / Java? Quero dizer “estender” tanto no sentido de adicionar novos valores a um enum, mas também no sentido OO de herdar de um enum existente.

Eu suponho que não é possível em Java, já que só os tem bastante recentemente (Java 5?). C # parece mais indulgente com pessoas que querem fazer coisas malucas, então eu achei que seria possível de alguma forma. Presumivelmente, ele poderia ser cortado por meio de reflection (não que você usasse realmente esse método)?

Eu não estou necessariamente interessado em implementar qualquer método dado, apenas provocou a minha curiosidade quando me ocorreu 🙂

A razão pela qual você não pode estender o Enums é porque isso levaria a problemas com o polymorphism.

Digamos que você tenha um enum MyEnum com valores A, B e C e estenda-o com o valor D como MyExtEnum.

Suponha que um método espere um valor myEnum em algum lugar, por exemplo, como um parâmetro. Deve ser legal fornecer um valor MyExtEnum, porque é um subtipo, mas agora o que você vai fazer quando o valor for D?

Para eliminar esse problema, estender enums é ilegal

Quando enums embutidos não são suficientes, você pode fazê-lo da maneira antiga e criar o seu próprio. Por exemplo, se você quisesse adicionar uma propriedade adicional, por exemplo, um campo de descrição, poderia fazê-lo da seguinte maneira:

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

Você pode então tratá-lo como um enum como:

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

O truque é garantir que o construtor seja privado (ou protegido se você quiser herdar) e que suas instâncias sejam estáticas.

Você está indo na direção errada: uma subclass de um enum teria menos inputs.

No pseudocódigo, pense:

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

Qualquer método que aceite um Animal deve ser capaz de aceitar um Mamífero, mas não o contrário. Subclasss é para fazer algo mais específico, não mais geral. É por isso que “object” é a raiz da hierarquia de classs. Da mesma forma, se enums fossem herdáveis, então uma raiz hipotética da hierarquia enum teria todos os símbolos possíveis.

Mas não, C # / Java não permite sub-enums, AFAICT, embora seja realmente útil às vezes. É provavelmente porque eles escolheram implementar Enums como ints (como C) em vez de símbolos internos (como Lisp). (Acima, o que (Animal) 1 representa, e o que (Mammal) 1 representa, e eles têm o mesmo valor?)

Você pode escrever sua própria class enum (com um nome diferente) que forneceu isso, no entanto. Com atributos do C #, pode até parecer legal.

Supõe-se que os enums representem a enumeração de todos os valores possíveis, de modo que a extensão, em vez disso, vai contra a ideia.

No entanto, o que você pode fazer em Java (e presumivelmente C ++ 0x) é ter uma interface em vez de uma class enum. Em seguida, coloque seus valores padrão em um enum que implemente o recurso. Obviamente você não consegue usar java.util.EnumSet e similares. Essa é a abordagem adotada em “mais resources do NIO”, que deve estar no JDK7.

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

Você pode usar a reflection .NET para recuperar os labels e valores de um enum existente em tempo de execução ( Enum.GetNames() e Enum.GetValues() são os dois methods específicos que você usaria) e, em seguida, usar injeção de código para criar um novo um com esses elementos e alguns novos. Isso parece um pouco análogo a “herdar de um enum existente”.

Adicionar enums é algo bastante comum se você voltar ao código-fonte e editar, de qualquer outra forma (inheritance ou reflection, se qualquer um deles for possível) provavelmente voltará e o atingirá quando você fizer um upgrade da biblioteca e eles introduziram o mesmo nome de enum ou o mesmo valor enum – eu vi muitos códigos de baixo nível onde o número inteiro corresponde à codificação binária, onde você teria problemas

Idealmente enums de referência de código devem ser escritas como iguais (ou comutadores), e tentar ser prova futura, não esperando que o enum definido para ser const

Eu não vi mais ninguém mencionar isso, mas o valor ordinal de um enum é importante. Por exemplo, com grails quando você salva um enum no database, ele usa o valor ordinal. Se você pudesse estender um enum, quais seriam os valores ordinais de suas extensões? Se você estendeu isso em vários lugares, como você poderia preservar algum tipo de ordem para esses ordinais? Caos / instabilidade nos valores ordinais seria uma coisa ruim, que é provavelmente outra razão pela qual os projetistas de linguagem não tocaram nisso.

Outra dificuldade se você fosse o designer de linguagem, como você pode preservar a funcionalidade do método values ​​() que deve retornar todos os valores de enum. Em que você invocaria isso e como reuniria todos os valores?

Se você quer dizer extends no sentido da class Base, então em Java … não.

Mas você pode estender um valor enum para ter propriedades e methods, se é isso que você quer dizer.

Por exemplo, o seguinte usa um enum de colchetes:

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

Vi um post sobre isso para Java um tempo atrás, confira http://www.javaspecialists.eu/archive/Issue161.html .

Você não pode herdar / estender um enum, você pode usar atributos para declarar uma descrição . Se você está procurando por um valor inteiro, isso é embutido.

Hmmm – até onde eu sei, isso não pode ser feito – as enumerações são escritas em tempo de design e são usadas como uma conveniência para o programador.

Tenho certeza de que quando o código é compilado, os valores equivalentes serão substituídos pelos nomes em sua enumeração, removendo assim o conceito de uma enumeração e (portanto) a capacidade de estendê-lo.

Gostaria de poder adicionar valores a enumerações C #, que são combinações de valores existentes. Por exemplo (isto é o que eu quero fazer):

AnchorStyles é definido como

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

e eu gostaria de adicionar um AnchorStyles.BottomRight = Right + Bottom então em vez de dizer

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

Posso apenas dizer

 my_ctrl.Anchor = AnchorStyles.BottomRight; 

Isso não causa nenhum dos problemas mencionados acima, então seria bom se fosse possível.

No que diz respeito ao java, isso não é permitido, pois a inclusão de elementos em um enum criaria efetivamente uma superclass em vez de uma subclass.

Considerar:

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

Um caso de uso geral de Polimorfismo seria

  Person person = Student.ROSS; //not legal 

que está claramente errado.

Algum tempo atrás, até eu queria fazer algo assim e descobri que extensões enum voilavam muitos conceitos básicos … (não apenas polimorphisim)

Mas você ainda pode precisar fazer se o enum for declarado na biblioteca externa e lembrar que você deve tomar um cuidado especial ao usar essas extensões de enum …

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