Я знаю, что это скорее противоречит идее перечислений, но можно ли расширить перечисления в C# / Java? Я имею в виду «расширить» как в смысле добавления новых значений в перечисление, так и в объектно-ориентированном смысле наследования от существующего перечисления.
Я предполагаю, что это невозможно в Java, поскольку они появились совсем недавно (Java 5?). C# кажется более снисходительным к людям, которые хотят делать сумасшедшие вещи, поэтому я подумал, что это может быть возможно. Предположительно, его можно взломать с помощью отражения (не то чтобы вы действительно использовали этот метод)?
Меня не обязательно интересует реализация того или иного метода, это просто вызвало у меня любопытство, когда это пришло мне в голову :-)





Вы не можете наследовать / расширять перечисление, вы можете использовать атрибуты для объявить описание. Если вы ищете целочисленное значение, оно встроено.
Хммм - насколько я знаю, это невозможно - перечисления пишутся во время разработки и используются для удобства программиста.
Я почти уверен, что при компиляции кода эквивалентные значения будут заменены именами в вашем перечислении, тем самым удалив концепцию перечисления и (следовательно) возможность его расширения.
Добавление перечислений - довольно распространенная вещь, которую можно сделать, если вы вернетесь к исходному коду и отредактируете, любой другой способ (наследование или отражение, если любое из них возможно), скорее всего, вернется и ударит вас, когда вы получите обновление библиотеки и они ввели одно и то же имя перечисления или же одно и то же значение перечисления - я видел много кода низкого уровня, где целое число соответствует двоичной кодировке, где вы столкнетесь с проблемами
В идеале перечисления, ссылающиеся на код, должны быть записаны только как равные (или переключатели) и попытаться быть перспективными, не ожидая, что набор перечислений будет константным.
Вы можете использовать отражение .NET для извлечения меток и значений из существующего перечисления во время выполнения (Enum.GetNames() и Enum.GetValues() - это два конкретных метода, которые вы могли бы использовать), а затем использовать внедрение кода для создания нового с этими элементами и некоторыми новыми . Это похоже на «наследование от существующего перечисления».
Причина, по которой вы не можете расширить Enums, заключается в том, что это приведет к проблемам с полиморфизмом.
Скажем, у вас есть перечисление MyEnum со значениями A, B и C и расширите его значением D как MyExtEnum.
Предположим, метод ожидает где-то значение myEnum, например, в качестве параметра. Предоставление значения MyExtEnum должно быть законным, потому что это подтип, но что теперь вы собираетесь делать, когда выясняется, что значение - D?
Чтобы устранить эту проблему, расширение перечислений является незаконным.
На самом деле причина просто в том, что в этом нет никакого смысла. Проблема, о которой вы упомянули, т.е. клиентский код, получающий константу, которую он не ожидает, все еще существует с текущей реализацией - на самом деле компилятор не позволяет вам switch для значений перечисления, не предоставив регистр default или не выбросив исключение. И даже если бы это было так, перечисление во время выполнения могло бы поступить из отдельной компиляции
@Raffaele Я только что попробовал это, и, по крайней мере, в Java 7 вы можете включить перечисление без случая default или бросания.
@Dathan, ты определенно прав! Как написано, это просто неправильно - может, стоит убрать? Я думал о случае, когда вы используете switch для предоставления необходимого значения, такого как инициал локального или return
Это не личное, Рик. Но это в значительной степени худшая причина на свете! Полиморфизм и перечисление ... DayOfWeek a = (DayOfWeek) 1; DayOfWeek b = (DayOfWeek) 4711; Console.WriteLine(a + ", " + b);
Когда встроенных перечислений недостаточно, вы можете сделать это по старинке и создать свои собственные. Например, если вы хотите добавить дополнительное свойство, например поле описания, вы можете сделать это следующим образом:
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#, но разве вы не хотите, чтобы там было немного final (или запечатанного / const / чего-то еще)?
Я так не верю. Или, по крайней мере, не для ситуации, когда вы хотите наследовать от этого класса, чтобы добавить новые значения «перечисления». Окончательное и запечатанное предотвращение наследования IIRC.
@alastairs Я думаю, он имел в виду добавление final к строке public static Action DoIt = new Action("Do it", "This does things");, а не к классу.
Например, добавив readonly, ala: public static readonly Action DoIt = new Action("Do it", "This does things");
Если вы имеете в виду расширение в смысле базового класса, то в 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;
}
}
Это звучит как тип значения (структура) в .NET. Я не знал, что вы можете сделать это на Java.
Предполагается, что перечисления представляют собой перечисление всех возможных значений, поэтому расширение скорее противоречит идее.
Однако то, что вы можете сделать в Java (и, предположительно, C++ 0x), - это иметь интерфейс вместо класса перечисления. Затем поместите стандартные значения в перечисление, реализующее эту функцию. Очевидно, вы не можете использовать 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
}
Некоторое время назад видел сообщение об этом для Java, посмотрите http://www.javaspecialists.eu/archive/Issue161.html.
Вы идете неправильным путем: подкласс перечисления будет иметь записи меньше.
В псевдокоде подумайте:
enum Animal { Mosquito, Dog, Cat };
enum Mammal : Animal { Dog, Cat }; // (not valid C#)
Любой метод, который может принимать Животное, должен уметь принимать Млекопитающее, но не наоборот. Создание подклассов предназначено для создания чего-то более конкретного, а не более общего. Вот почему «объект» является корнем иерархии классов. Точно так же, если бы перечисления были наследуемыми, то гипотетический корень иерархии перечислений имел бы все возможные символы.
Но нет, C# / Java не допускают вложенных перечислений AFAICT, хотя иногда это может быть действительно полезно. Вероятно, это потому, что они решили реализовать Enums как целые числа (например, C) вместо интернированных символов (например, Lisp). (Выше, что представляет (Животное) 1 и что представляет (Млекопитающее) 1, и имеют ли они одно и то же значение?)
Однако вы могли бы написать свой собственный enum-подобный класс (с другим именем), который бы это обеспечил. С атрибутами C# это могло бы даже выглядеть неплохо.
интересный анализ. никогда не думал об этом так!
Интересно, но я считаю, что это неправильно. Предполагать, что в подклассе должно быть меньше типов, имеет смысл с точки зрения деревьев классификации животных, но не с точки зрения кода. Когда вы создаете подкласс в коде, методов никогда не становится меньше. Переменных-членов меньше никогда не бывает. Ваш аргумент, похоже, неприменим к программному обеспечению, как к классификации животных.
@Kieveli, ваш анализ неверен. Добавление члена не оказывает на объект такого же эффекта, как добавление к набору его возможных значений. Это не выдумал Кен; существуют ясные и точные правила информатики, объясняющие, почему это работает именно так. Попробуйте поискать ковариацию и контравариантность (не только академическую мастурбацию, это поможет вам понять правила для таких вещей, как сигнатуры функций и контейнеры).
@Kieveli Предположим, у вас есть класс StreamWriter (принимает поток - записывает поток). Вы бы создали TextWriter: StreamWriter (берет поток - записывает текст). Теперь вы создаете HtmlWriter: TextWriter (берет поток - пишет красивый html). Теперь у HtmlWriter явно больше членов, методов и так далее. Теперь попробуйте взять поток со звуковой карты и записать его в файл с помощью HtmlWriter :)
Этот пример надуман и не доказывает, что в расширенном классе никогда не должно быть БОЛЬШЕ значений перечисления. enum VehicalParts {Лицензия, Скорость, Емкость}; enum CarParts {SteeringWheel, Winshield}; + все, что есть в Vehical
Я не видел, чтобы кто-то еще упоминал об этом, но порядковый номер перечисления важен. Например, с помощью grails, когда вы сохраняете перечисление в базе данных, оно использует порядковое значение. Если бы вы могли как-то расширить перечисление, каковы были бы порядковые значения ваших расширений? Если вы расширили его в нескольких местах, как бы вы могли сохранить какой-то порядок в этих порядковых номерах? Хаос / нестабильность в порядковых значениях - это плохо, что, вероятно, является еще одной причиной, по которой разработчики языка не коснулись этого.
Еще одна трудность, если вы были разработчиком языка, как сохранить функциональность метода values (), который должен возвращать все значения перечисления. Что бы вы использовали для этого и как бы он собрал все ценности?
Я хотел бы иметь возможность добавлять значения в перечисления 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;
Это не вызывает никаких проблем, которые были упомянуты выше, поэтому было бы неплохо, если бы это было возможно.
Что касается java, это запрещено, потому что добавление элементов в перечисление фактически создало бы суперкласс, а не подкласс.
Учитывать:
enum Person (JOHN SAM}
enum Student extends Person {HARVEY ROSS}
Общий вариант использования полиморфизма:
Person person = Student.ROSS; //not legal
что явно неверно.
Некоторое время назад я даже хотел сделать что-то подобное и обнаружил, что расширения 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;
}
}
Временный / локальный обходной путь, когда вам просто нужно очень локальное / одноразовое использование:
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 "Наследование"
Отвечает ли это на ваш вопрос? Enum "Наследование"