Расширение перечисления через наследование
Я знаю, что это скорее противоречит идее перечислений, но можно ли расширить перечисления в C#/Java? Я имею в виду" расширить " как в смысле добавления новых значений в перечисление, так и в смысле наследования от существующего перечисления.
Я предполагаю, что это невозможно в Java, так как он получил их совсем недавно (Java 5?). C# кажется более снисходительным к людям, которые хотят делать сумасшедшие вещи, хотя, поэтому я подумал, что это может быть возможно каким-то образом. Предположительно он может быть взломан через отражение (не то, что вы бы все на самом деле использовать этот метод)?
Я не обязательно заинтересован в реализации любого данного метода, он просто вызвал мое любопытство, когда это пришло мне в голову : -)
14 ответов:
причина, по которой Вы не можете расширить перечисления, заключается в том, что это приведет к проблемам с полиморфизмом.
скажем, у вас есть перечисление MyEnum со значениями A, B и C , и расширить его со значением D как MyExtEnum.
предположим, что метод ожидает значение myEnum где-то, например, в качестве параметра. Должно быть законно предоставить значение MyExtEnum, потому что это подтип, но теперь что вы собираетесь делать, когда выяснится, что значение D?
чтобы устранить эту проблему, расширение перечислений является незаконным
когда встроенных перечислений недостаточно, вы можете сделать это по-старому и создать свой собственный. Например, если вы хотите добавить дополнительное свойство, например, поле описания, вы можете сделать это следующим образом:
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 } }
хитрость заключается в том, чтобы убедиться, что конструктор является частным (или защищенным, если вы хотите наследовать), и что ваши экземпляры статичны.
вы идете не в ту сторону: подкласс перечисления будет иметь меньше записи.
в псевдокод, думаю:
enum Animal { Mosquito, Dog, Cat }; enum Mammal : Animal { Dog, Cat }; // (not valid C#)
любой метод, который может принять животное, должен быть способен принять млекопитающее, но не наоборот. Подклассы предназначены для создания чего-то более конкретного, а не более общего. Вот почему "объект" является корнем иерархии классов. Аналогично, если перечисления наследуются, то гипотетический корень иерархии перечислений будет иметь каждый возможный символ.
но нет, C# / Java не допускают суб-перечислений, AFAICT, хотя это было бы действительно полезно время от времени. Вероятно, это потому, что они решили реализовать перечисления как ints (например, C) вместо интернированных символов (например, Lisp). (Выше, что представляет собой (животное)1 и что представляет собой (млекопитающее)1, и являются ли они одной и той же ценностью?)
вы можете написать свой собственный enum-подобный класс (с другим именем), который предоставил это. С атрибутами C# это может даже выглядеть как милый.
перечисления должны представлять собой перечисление всех возможных значений, поэтому расширение скорее идет вразрез с идеей.
однако то, что вы можете сделать в Java (и предположительно C++0x), имеет интерфейс вместо класса enum. Затем поместите стандартные значения в перечисление, которое реализует эту функцию. Очевидно, что вы не можете использовать java.утиль.EnumSet и тому подобное. Это подход, принятый в "more NIO features", который должен быть в 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()
это два конкретных метода, которые вы будете использовать), а затем использовать инъекцию кода для создания нового с этими элементами плюс некоторые новые. Это кажется несколько аналагичным для "наследования от существующего перечисления".
добавление перечислений-довольно распространенная вещь, если вы вернетесь к исходному коду и отредактируете, любой другой способ (наследование или отражение, если это возможно), скорее всего, вернется и ударит вас, когда вы получите обновление библиотеки, и они ввели одно и то же имя перечисления или те же значения enum - я видел много низкоуровневого кода, где целое число соответствует двоичному кодированию, где вы столкнетесь с проблемами
В идеале код ссылки перечисления должно быть записано только как равные (или переключатели), и постарайтесь быть будущим доказательством, не ожидая, что набор перечислений будет const
Я не видел, чтобы кто-то еще упоминал об этом, но порядковое значение перечисления важно. Например, в grails при сохранении перечисления в базе данных используется порядковое значение. Если бы вы могли каким-то образом расширить перечисление, каковы были бы порядковые значения ваших расширений? Если вы расширили его в нескольких местах, как вы могли бы сохранить какой-то порядок для этих ординалов? Хаос / нестабильность в порядковых значениях было бы плохо, что, вероятно, еще одна причина, по которой разработчики языка не прикасались к этому.
еще одна трудность если бы Вы были дизайнером языка, как вы можете сохранить функциональность метода values (), который должен возвращать все значения перечисления. Что бы вы вызвали на это и как бы он собрал все ценности?
Если вы имеете в виду extends в смысле базового класса, то в 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; } }
видел сообщение об этом для 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 so вместо того, чтобы сказать
my_ctrl.Anchor = AnchorStyles.Right | AnchorStyles.Bottom;
Я могу просто сказать
my_ctrl.Anchor = AnchorStyles.BottomRight;
Это не вызывает никаких проблем, которые были упомянуты выше, так что было бы неплохо, если бы он был вероятный.
что касается java, это не допускается, потому что добавление элементов в перечисление эффективно создаст суперкласс, а не подкласс.
считаем:
enum Person (JOHN SAM} enum Student extends First {HARVEY ROSS}
общий случай использования полиморфизма будет
Person person = Student.ROSS; //not legal
что явно неправильно.
некоторое время назад даже я хотел сделать что-то вроде этого и обнаружил, что расширения enum будут вуалировать много основных понятий... (Не только полиморфизм)
но все же u может потребоваться сделать, если перечисление объявлено во внешней библиотеке и Помните, что вы должны сделать особую осторожность при использовании этого перечисления расширений...
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; } }