Я знаю, что это скорее противоречит идее перечислений, но можно ли расширить перечисления в C # / Java? Я имею в виду «расширить» как в смысле добавления новых значений в перечисление, так и в объектно-ориентированном смысле наследования от существующего перечисления.
Я предполагаю, что это невозможно в Java, поскольку они появились совсем недавно (Java 5?). Однако C # кажется более снисходительным к людям, которые хотят делать сумасшедшие вещи, поэтому я подумал, что это может быть возможно. Предположительно, это могло быть взломано через отражение (не то чтобы вы действительно использовали этот метод)?
Я не обязательно заинтересован в реализации какого-либо конкретного метода, это просто вызвало мое любопытство, когда это пришло мне в голову :-)
Ответы:
Причина, по которой вы не можете расширить Enums, заключается в том, что это приведет к проблемам с полиморфизмом.
Скажем, у вас есть перечисление MyEnum со значениями A, B и C и расширите его значением D как MyExtEnum.
Предположим, метод ожидает где-то значение myEnum, например, в качестве параметра. Предоставление значения MyExtEnum должно быть законным, потому что это подтип, но что теперь вы собираетесь делать, когда выясняется, что значение равно D?
Чтобы устранить эту проблему, расширение перечислений запрещено
источник
switch
значения перечисления без указанияdefault
случая или исключения исключения. И даже если бы это было так, перечисление во время выполнения могло бы поступать из отдельной компиляцииdefault
случая или бросания.switch
чтобы обеспечить требуемое значение, например, инициал местных, илиreturn
DayOfWeek a = (DayOfWeek) 1; DayOfWeek b = (DayOfWeek) 4711; Console.WriteLine(a + ", " + b);
Вы идете неправильным путем: в подклассе перечисления будет меньше записей.
В псевдокоде подумайте:
enum Animal { Mosquito, Dog, Cat }; enum Mammal : Animal { Dog, Cat }; // (not valid C#)
Любой метод, который может принимать Животное, должен уметь принимать Млекопитающее, но не наоборот. Создание подклассов предназначено для создания чего-то более конкретного, а не более общего. Вот почему «объект» является корнем иерархии классов. Точно так же, если бы перечисления были наследуемыми, то гипотетический корень иерархии перечислений имел бы все возможные символы.
Но нет, C # / Java не допускают вложенных перечислений AFAICT, хотя иногда это было бы действительно полезно. Вероятно, это потому, что они решили реализовать Enums как int (например, C) вместо интернированных символов (например, Lisp). (Выше, что представляет (Животное) 1 и что представляет (Млекопитающее) 1, и имеют ли они одно и то же значение?)
Однако вы могли бы написать свой собственный enum-подобный класс (с другим именем), который это обеспечил бы. С атрибутами C # это могло бы даже выглядеть неплохо.
источник
Когда встроенных перечислений недостаточно, вы можете сделать это по старинке и создать свои собственные. Например, если вы хотите добавить дополнительное свойство, например поле описания, вы можете сделать это следующим образом:
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 } }
Хитрость заключается в том, чтобы убедиться, что конструктор является частным (или защищенным, если вы хотите наследовать), и что ваши экземпляры статичны.
источник
final
кpublic static Action DoIt = new Action("Do it", "This does things");
строке, а не к классу.readonly
public static readonly Action DoIt = new Action("Do it", "This does things");
Предполагается, что перечисления представляют собой перечисление всех возможных значений, поэтому расширение скорее противоречит идее.
Однако то, что вы можете сделать в Java (и, предположительно, C ++ 0x), - это иметь интерфейс вместо класса enum. Затем поместите стандартные значения в перечисление, реализующее эту функцию. Очевидно, вы не можете использовать java.util.EnumSet и тому подобное. Это подход, принятый в «дополнительных функциях NIO», которые должны быть в JDK7.
public interface Result { String name(); String toString(); } public enum StandardResults implements Result { TRUE, FALSE } public enum WTFResults implements Result { FILE_NOT_FOUND }
источник
Вы можете использовать отражение .NET для извлечения меток и значений из существующего перечисления во время выполнения (
Enum.GetNames()
иEnum.GetValues()
это два конкретных метода, которые вы могли бы использовать), а затем использовать внедрение кода для создания нового с этими элементами и некоторыми новыми. Это несколько похоже на «наследование от существующего перечисления».источник
Добавление перечислений - довольно распространенная вещь, которую можно сделать, если вы вернетесь к исходному коду и отредактируете, любой другой способ (наследование или отражение, если любое из них возможно), скорее всего, вернется и ударит вас, когда вы получите обновление библиотеки и они ввели одно и то же имя перечисления или одно и то же значение перечисления - я видел много кода низкого уровня, где целое число соответствует двоичной кодировке, где вы столкнетесь с проблемами
В идеале перечисления, ссылающиеся на код, должны быть написаны только как равные (или переключатели) и попытаться быть перспективными, не ожидая, что набор перечислений будет константным.
источник
Если вы имеете в виду расширение в смысле базового класса, то в Java ... нет.
Но вы можете расширить значение перечисления, чтобы иметь свойства и методы, если вы это имеете в виду.
Например, в следующем примере используется перечисление Bracket:
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; } }
источник
Я не видел, чтобы кто-то еще упоминал об этом, но порядковый номер перечисления важен. Например, с grails, когда вы сохраняете перечисление в базе данных, оно использует порядковое значение. Если бы вы могли каким-то образом расширить перечисление, каковы были бы порядковые значения ваших расширений? Если вы расширили его в нескольких местах, как бы вы могли сохранить какой-то порядок в этих порядковых номерах? Хаос / нестабильность в порядковых значениях - это плохо, что, вероятно, является еще одной причиной, по которой разработчики языка не коснулись этого.
Еще одна трудность, если вы были разработчиком языка, как сохранить функциональность метода values (), который должен возвращать все значения перечисления. Что бы вы использовали для этого и как бы он собрал все ценности?
источник
Некоторое время назад видел сообщение об этом для Java, посетите http://www.javaspecialists.eu/archive/Issue161.html .
источник
Вы не можете наследовать / расширять перечисление, вы можете использовать атрибуты для объявления описания . Если вы ищете целочисленное значение, оно встроено.
источник
Хммм - насколько я знаю, это невозможно - перечисления пишутся во время разработки и используются для удобства программиста.
Я почти уверен, что при компиляции кода эквивалентные значения будут заменены именами в вашем перечислении, тем самым удалив концепцию перечисления и (следовательно) возможность его расширения.
источник
Я хотел бы иметь возможность добавлять значения в перечисления 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;
Это не вызывает никаких проблем, которые были упомянуты выше, поэтому было бы неплохо, если бы это было возможно.
источник
Некоторое время назад я даже хотел сделать что-то подобное и обнаружил, что расширения 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; } }
источник
Что касается java, это запрещено, потому что добавление элементов в перечисление фактически создало бы суперкласс, а не подкласс.
Рассматривать:
enum Person (JOHN SAM} enum Student extends Person {HARVEY ROSS}
Общий вариант использования полиморфизма:
Person person = Student.ROSS; //not legal
что явно неверно.
источник
Временный / локальный обходной путь , когда вам просто нужно очень локальное / одноразовое использование:
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 );
Смотрите все ответы в: Enum "Inheritance"
источник