Расширение перечисления через наследование

Я знаю, что это скорее противоречит идее перечислений, но возможно ли продлить перечисления на С#/Java? Я имею в виду "расширение" как в смысле добавления новых значений в перечисление, так и в смысле OO наследования от существующего перечисления.

Я предполагаю, что это невозможно в Java, так как он получил их довольно недавно (Java 5?). С# кажется более прощающим людей, которые хотят делать сумасшедшие вещи, поэтому я подумал, что это возможно в некотором роде. Предположительно, его можно было взломать через отражение (не то, чтобы вы каждый использовали этот метод)?

Я не обязательно заинтересован в реализации какого-либо данного метода, это просто вызвало мое любопытство, когда это пришло мне в голову: -)

Ответ 1

Причина, по которой вы не можете расширять Enums, связана с тем, что это приведет к проблемам с полиморфизмом.

Скажем, у вас есть перечисление MyEnum со значениями A, B и C и расширьте его значением D как MyExtEnum.

Предположим, что метод ожидает значение myEnum где-то, например, в качестве параметра. Должно быть законным предоставлять значение MyExtEnum, потому что это подтип, но теперь, что вы собираетесь делать, когда получится значение D?

Чтобы устранить эту проблему, расширение перечислений является незаконным

Ответ 2

Когда встроенных перечислений недостаточно, вы можете сделать это по-старому и создать свой собственный. Например, если вы хотите добавить дополнительное свойство, например, поле описания, вы можете сделать это следующим образом:

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

Трюк заключается в том, чтобы убедиться, что конструктор является закрытым (или защищенным, если вы хотите наследовать), и что ваши экземпляры являются статическими.

Ответ 3

Вы ошибетесь: в подклассе перечисления будет меньше записей.

В псевдокоде подумайте:

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

Любой метод, который может принять Животное, должен иметь возможность принимать Млекопитающее, но не наоборот. Подкласс предназначен для создания чего-то более конкретного, не более общего. Поэтому "объект" является корнем иерархии классов. Аналогично, если перечисления были наследованы, тогда гипотетический корень иерархии перечислений имел бы все возможные символы.

Но нет, С#/Java не разрешают sub-enums, AFAICT, хотя иногда это было бы очень полезно. Вероятно, потому, что они решили реализовать Enums как ints (например, C) вместо интернированных символов (например, Lisp). (Выше, что означает (Animal) 1, и что означает (Mammal) 1, и являются ли они одним и тем же значением?)

Вы могли бы написать свой собственный enum-подобный класс (с другим именем), который предоставил это. С атрибутами С# это может показаться даже приятным.

Ответ 4

Предполагается, что перечисления представляют собой перечисление всех возможных значений, поэтому распространение скорее противоречит идее.

Однако, что вы можете сделать в Java (и предположительно С++ 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
}

Ответ 5

Вы можете использовать отражение .NET для извлечения ярлыков и значений из существующего перечисления во время выполнения (Enum.GetNames() и Enum.GetValues() - это два конкретных метода, которые вы должны использовать), а затем использовать ввод кода для создания нового с этими элементами плюс некоторые новые. Это кажется несколько аналогичным "наследованию от существующего перечисления".

Ответ 6

Добавление перечислений - довольно обычная вещь, если вы вернетесь к исходному коду и отредактируете, любым другим способом (наследование или отражение, если это возможно), скорее всего, вернется и ударит вас, когда вы получите обновление в библиотеке, и они представили одно и то же имя перечисления или одно и то же значение перечисления - я видел много кода низкого уровня, где целое число соответствует двоичной кодировке, где вы столкнулись с проблемами

В идеале ссылки на привязку кодов должны записываться только как равные (или переключатели) и пытаться быть будущими доказательствами, не ожидая, что значение перечисления будет const

Ответ 7

Если вы имеете в виду расширения в смысле базового класса, то в Java... нет.

Но вы можете расширить значение enum, чтобы иметь свойства и методы, если это то, что вы имеете в виду.

Например, следующее перечисляет перечисление с помощью скобки:

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

Ответ 8

Я не видел, чтобы кто-то еще упоминал об этом, но значение параметра enum важно. Например, с граалями при сохранении перечисления в базе данных он использует порядковое значение. Если бы вы могли каким-то образом расширить перечисление, какими будут порядковые значения ваших расширений? Если вы расширили его в нескольких местах, как бы вы могли сохранить какой-то порядок для этих ординалов? Хаос/нестабильность в порядковых значениях будет плохой, что, вероятно, является еще одной причиной, по которой разработчики языка не коснулись этого.

Еще одна сложность, если вы были разработчиком языка, как вы можете сохранить функциональность метода values ​​(), который должен возвращать все значения перечисления. Что бы вы вызвали это и как бы он собрал все значения?

Ответ 10

Вы не можете наследовать/расширять перечисление, вы можете использовать атрибуты для объявить описание. Если вы ищете целочисленное значение, это встроенное.

Ответ 11

Хммм - насколько я знаю, это невозможно сделать - перечисления записываются во время разработки и используются для удобства программиста.

Я уверен, что когда код будет скомпилирован, эквивалентные значения будут заменены именами в вашем перечислении, тем самым устраняя концепцию перечисления и (следовательно) возможность ее расширения.

Ответ 12

Я хотел бы иметь возможность добавлять значения в перечисления С#, которые являются комбинациями существующих значений. Например (это то, что я хочу сделать):

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;

Это не вызывает никаких проблем, о которых говорилось выше, поэтому было бы неплохо, если бы это было возможно.

Ответ 13

Что касается Java, то это не разрешено, потому что добавление элементов в перечисление будет эффективно создавать суперкласс, а не подкласс.

Рассматривать:

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

Общий случай использования полиморфизма будет

 Person person = Student.ROSS;   //not legal

что явно неправильно.

Ответ 14

Некоторое время назад даже я хотел сделать что-то вроде этого и обнаружил, что расширения 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;
   }
}