Расширение перечисления через наследование - PullRequest
78 голосов
/ 11 сентября 2008

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

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

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

Ответы [ 14 ]

97 голосов
/ 11 сентября 2008

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

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

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

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

40 голосов
/ 11 сентября 2008

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

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

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

38 голосов
/ 06 ноября 2009

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

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

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

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

Но нет, C # / Java не допускают под-перечислений, AFAICT, хотя иногда это было бы действительно полезно. Вероятно, потому, что они решили реализовать Enums в виде целых (например, C) вместо интернированных символов (например, Lisp). (Выше, что представляет (Животное) 1, и что представляет (Млекопитающее) 1, и имеют ли они одинаковое значение?)

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

12 голосов
/ 11 сентября 2008

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

Однако то, что вы можете сделать в 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
}
3 голосов
/ 11 сентября 2008

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

2 голосов
/ 12 декабря 2009

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

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

2 голосов
/ 11 сентября 2008

Если вы имеете в виду расширение в смысле базового класса, то в 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;
    }        
}
2 голосов
/ 11 сентября 2008

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

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

1 голос
/ 11 сентября 2008

Видел пост об этом для Java некоторое время назад, посмотрите http://www.javaspecialists.eu/archive/Issue161.html.

0 голосов
/ 17 декабря 2014

Некоторое время назад, даже когда я захотел сделать что-то подобное, я обнаружил, что расширения enum аннулируют множество базовых понятий ... (не только полиморфизм)

Но, тем не менее, вам может понадобиться, если 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;
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...