C# против Java Enum (для новичков в C#)

Некоторое время я программировал на Java, и меня просто бросили в проект, полностью написанный на C#. Я пытаюсь разобраться в C# и заметил, что перечисления используются в нескольких местах в моем новом проекте, но на первый взгляд перечисления C# кажутся более упрощенными, чем реализация Java 1.5+. Может ли кто-нибудь перечислить различия между перечислениями C# и Java и как их преодолеть? (Я не хочу начинать войну языкового пламени, я просто хочу знать, как делать некоторые вещи на C#, которые я делал на Java). Например, может ли кто-нибудь опубликовать аналог на C# известного примера перечисления Sun Planet?

public enum Planet {
  MERCURY (3.303e+23, 2.4397e6),
  VENUS   (4.869e+24, 6.0518e6),
  EARTH   (5.976e+24, 6.37814e6),
  MARS    (6.421e+23, 3.3972e6),
  JUPITER (1.9e+27,   7.1492e7),
  SATURN  (5.688e+26, 6.0268e7),
  URANUS  (8.686e+25, 2.5559e7),
  NEPTUNE (1.024e+26, 2.4746e7),
  PLUTO   (1.27e+22,  1.137e6);

  private final double mass;   // in kilograms
  private final double radius; // in meters
  Planet(double mass, double radius) {
      this.mass = mass;
      this.radius = radius;
  }
  public double mass()   { return mass; }
  public double radius() { return radius; }

  // universal gravitational constant  (m3 kg-1 s-2)
  public static final double G = 6.67300E-11;

  public double surfaceGravity() {
      return G * mass / (radius * radius);
  }
  public double surfaceWeight(double otherMass) {
      return otherMass * surfaceGravity();
  }
}

// Example usage (slight modification of Sun's example):
public static void main(String[] args) {
    Planet pEarth = Planet.EARTH;
    double earthRadius = pEarth.radius(); // Just threw it in to show usage

    // Argument passed in is earth Weight.  Calculate weight on each planet:
    double earthWeight = Double.parseDouble(args[0]);
    double mass = earthWeight/pEarth.surfaceGravity();
    for (Planet p : Planet.values())
       System.out.printf("Your weight on %s is %f%n",
                         p, p.surfaceWeight(mass));
}

// Example output:
$ java Planet 175
Your weight on MERCURY is 66.107583
Your weight on VENUS is 158.374842
[etc ...]

@ycomp Я не могу поверить в это. Он исходит от Sun (теперь Oracle): docs.oracle.com/javase/tutorial/java/javaOO/enum.html

Ogre Psalm33 22.09.2015 16:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
189
1
68 494
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

Ответ принят как подходящий

Перечисления в CLR - это просто именованные константы. Базовый тип должен быть целым. В Java перечисление больше похоже на именованный экземпляр типа. Этот тип может быть довольно сложным и, как показывает ваш пример, содержать несколько полей различных типов.

Чтобы перенести пример на C#, я бы просто изменил перечисление на неизменяемый класс и предоставил статические экземпляры этого класса только для чтения:

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Planet planetEarth = Planet.MERCURY;

            double earthRadius = pEarth.Radius; // Just threw it in to show usage
            double earthWeight = double.Parse("123");
            double earthMass   = earthWeight / pEarth.SurfaceGravity();

            foreach (Planet p in Planet.Values)
                Console.WriteLine($"Your weight on {p} is {p.SurfaceWeight(mass)}");

            Console.ReadKey();
        }
    }

    public class Planet
    {
        public static readonly Planet MERCURY = new Planet("Mercury", 3.303e+23, 2.4397e6);
        public static readonly Planet VENUS   = new Planet("Venus", 4.869e+24, 6.0518e6);
        public static readonly Planet EARTH   = new Planet("Earth", 5.976e+24, 6.37814e6);
        public static readonly Planet MARS    = new Planet("Mars", 6.421e+23, 3.3972e6);
        public static readonly Planet JUPITER = new Planet("Jupiter", 1.9e+27, 7.1492e7);
        public static readonly Planet SATURN  = new Planet("Saturn", 5.688e+26, 6.0268e7);
        public static readonly Planet URANUS  = new Planet("Uranus", 8.686e+25, 2.5559e7);
        public static readonly Planet NEPTUNE = new Planet("Neptune", 1.024e+26, 2.4746e7);
        public static readonly Planet PLUTO   = new Planet("Pluto", 1.27e+22, 1.137e6);

        public static IEnumerable<Planet> Values
        {
            get
            {
                yield return MERCURY;
                yield return VENUS;
                yield return EARTH;
                yield return MARS;
                yield return JUPITER;
                yield return SATURN;
                yield return URANUS;
                yield return NEPTUNE;
                yield return PLUTO;
            }
        }

        public string Name   { get; private set; }
        public double Mass   { get; private set; }
        public double Radius { get; private set; }

        Planet(string name, double mass, double radius) => 
            (Name, Mass, Radius) = (name, mass, radius);

        // Wniversal gravitational constant  (m3 kg-1 s-2)
        public const double G = 6.67300E-11;
        public double SurfaceGravity()            => G * mass / (radius * radius);
        public double SurfaceWeight(double other) => other * SurfaceGravity();
        public override string ToString()         => name;
    }
}

Это означает, что вы не можете использовать цикл for-each в перечислениях.

Richard Walton 22.01.2009 17:30

Это своего рода типобезопасное перечисление, которое мы, бедняки, вынужденные использовать Java 1.4 и ниже, должны реализовать ... Перечисления Java 5, пожалуй, лучшая особенность Java 5+, тем более, что их можно использовать в операторах switch.

MetroidFan2002 22.01.2009 17:45

Хорошо, из того, что я получил до сих пор, этот мне нравится больше всего. Это похоже на то, о чем я думал: если вам нужны методы перечисления в C#, либо измените их на константы (как вы), либо вставьте перечисление в служебный класс (что может быть труднее сделать с примером «Планета») .

Ogre Psalm33 22.01.2009 20:35

Один очень незначительный момент заключается в том, что Microsoft предлагает использовать имена перечислений во множественном числе afaik.

Chris S 22.01.2009 23:50

@Chris: во множественном числе должны быть только перечисления флагов. То есть перечисления, члены которых объединяются с помощью | оператор.

Kent Boogaart 05.04.2009 13:10

Я все равно считаю этот пример глупым. Что, если вы захотите добавить новую планету? Вам придется отредактировать код. Вот почему вы должны использовать обычные классы для планет, а не перечисления в Java или C#.

Mladen Mihajlovic 07.04.2009 15:34

@Mladen: Это полностью зависит от контекста. Перечисление планет может идеально подходить для игры, которая предоставляет доступ к ограниченному количеству планет. Изменения в коде могут быть именно тем, что вам нужно, если в игру добавляется новая планета.

Kent Boogaart 07.04.2009 15:38

@Kent Верно, но мне все равно это не кажется хорошим дизайном. Если бы я хотел создать расширение для игры (например), я бы хотел, чтобы вся эта информация была отделена от исходного кода. Тем не менее, я не говорю, что для перечислений нет места, просто пример не подходит им в моих глазах.

Mladen Mihajlovic 08.04.2009 12:52

@Richie_W вы можете перебирать перечисления, используя свойство Values.

Jonathan 06.12.2010 18:57

@Richie_W у него есть чувство юмора. "Чужой подход", понятно?

Erix 09.12.2011 02:47

Ничего себе ... Почти невероятно увидеть что-то более подробное на C#, чем на Java.

Sune Rasmussen 25.09.2012 12:50

@KentBoogaart Разве это не идеально для структуры вместо класса?

nawfal 08.05.2013 17:37

Думаю, лучше вместо public static IEnumerable<Planet> Values использовать массив значений, что-то вроде private static Plan[] values = {VIP, CHEAP, MEDIUM, D_PLAN};. Тогда он будет ближе к перечислению Java (вы можете иметь длину и получать доступ к каждому перечислению с помощью индекса)

Fr4nz 10.07.2013 15:27

Используя этот пример, если бы вы хотели преобразовать имя планеты в виде строки в объект Planet, как бы вы это сделали? Я бы хотел сделать что-нибудь вроде var p = "Earth"; var enum = (Planet)p;.

user1636130 30.05.2016 18:48

@nawfal На первый взгляд мне так показалось, но, в отличие от C#, java enum может иметь значение null. Я думаю, что class более эквивалентен с этой точки зрения.

Attacktive 26.07.2016 07:52

@Suzi, Кроме того, в данном типе требуется double и double и ссылка, которая должна составлять до 20 байт. Не подходит для типа структуры, если он больше 16 байт, как правило, копирование и все такое. Мой первоначальный комментарий был довольно глупым :)

nawfal 26.07.2016 08:50

Однако нельзя использовать в коммутаторе :(

Sinaesthetic 23.08.2016 03:07

Это недоступно во время компиляции. Я не нашел способа использовать одну из планет в качестве значения по умолчанию для параметра функции. :(

Zorgarath 19.05.2017 22:38

Перечисление Java - это синтаксический сахар для представления перечислений в объектно-ориентированной манере. Это абстрактные классы, расширяющие класс Enum в Java, и каждое значение перечисления похоже на реализацию статического окончательного общедоступного экземпляра класса перечисления. Посмотрите на сгенерированные классы, и для перечисления «Foo» с 10 значениями вы увидите сгенерированные классы от «Foo $ 1» до «Foo $ 10».

Я не знаю C#, но могу только предположить, что перечисление на этом языке больше похоже на традиционное перечисление в языках стиля C. Я вижу из быстрого поиска в Google, что они могут содержать несколько значений, поэтому они, вероятно, реализованы аналогичным образом, но с гораздо большими ограничениями, чем позволяет компилятор Java.

Ну разве все в Java и C# не связано с синтаксическим сахаром для байт-кодов JVM или CLR? :) Просто говорю'

thenonhacker 25.05.2011 14:55

Перечисления Java на самом деле являются полными классами, которые могут иметь частный конструктор, методы и т. д., Тогда как перечисления C# - это просто целые числа. Реализация IMO Java намного превосходит.

Эта страница должна очень помочь вам при изучении C# из лагеря Java. (ссылка указывает на различия в перечислениях (прокрутите вверх / вниз для других вещей)

Хотя ваша ссылка дает интересный, обширный обзор сходств и различий между C# и Java, в тексте много ошибок (например, неверно указано, что Java protected соответствует внутреннему C#, тогда как он должен быть внутренним защищенным). Так что не принимайте все как должное :)

mafu 23.01.2009 14:39

Я бы не сказал, что перечисления Java лучше, даже если я фанат java. C# поддерживает простое целочисленное объявление, такое как FOO = 0, которое проще использовать в инструментах ORM (использование ordinal(), подверженного ошибкам, не требуется). Кроме того, C# поддерживает побитовые перечисления, которые часто очень полезны, особенно в сочетании с EntityFramework. Java должна расширить свои перечисления, чтобы также можно было связывать их с целыми числами. Тогда они были бы лучше :)

djmj 29.08.2015 03:05

Примерно так я думаю:

public class Planets 
{
    public static readonly Planet MERCURY = new Planet(3.303e+23, 2.4397e6);
    public static readonly Planet VENUS = new Planet(4.869e+24, 6.0518e6);
    public static readonly Planet EARTH = new Planet(5.976e+24, 6.37814e6);
    public static readonly Planet MARS = new Planet(6.421e+23, 3.3972e6);
    public static readonly Planet JUPITER = new Planet(1.9e+27,   7.1492e7);
    public static readonly Planet SATURN = new Planet(5.688e+26, 6.0268e7);
    public static readonly Planet URANUS = new Planet(8.686e+25, 2.5559e7);
    public static readonly Planet NEPTUNE = new Planet(1.024e+26, 2.4746e7);
    public static readonly Planet PLUTO = new Planet(1.27e+22,  1.137e6);
}

public class Planet
{
    public double Mass {get;private set;}
    public double Radius {get;private set;}

    Planet(double mass, double radius)
    {
        Mass = mass;
        Radius = radius;
    }

    // universal gravitational constant  (m3 kg-1 s-2)
    private static readonly double G = 6.67300E-11;

    public double SurfaceGravity()
    {
        return G * Mass / (Radius * Radius);
    }

    public double SurfaceWeight(double otherMass)
    {
        return otherMass * SurfaceGravity();
    }
}

Или объедините константы в класс Planet, как указано выше.

Не совсем так - конструктор Planet должен быть приватным; Отчасти суть перечислений в том, что они представляют собой фиксированный набор значений. Значения тогда также будут определены в классе Planet.

Jon Skeet 22.01.2009 18:06

Еще нет. 1) перечислитель отсутствует :) 2) перечисления никогда не должны изменяться. И, наконец, ваш код требует одного класса (особенно, когда у вас есть частный конструктор)

nawfal 08.05.2013 17:28

Перечисления Java позволяют легко преобразовывать типы из имени с помощью генерируемого компилятором метода valueOf, т. Е.

// Java Enum has generics smarts and allows this
Planet p = Planet.valueOf("MERCURY");

Эквивалент для необработанного перечисления в C# более подробен:

// C# enum - bit of hoop jumping required
Planet p = (Planet)Enum.Parse(typeof(Planet), "MERCURY");

Однако, если вы пойдете по маршруту, предложенному Кентом, вы можете легко реализовать метод ValueOf в своем классе перечисления.

В примере Java используется синтетический метод, сгенерированный компилятором - никакого отношения к дженерикам. Enum имеет общий метод valueOf, но он использует универсальные типы Class, а не Enum.

Tom Hawtin - tackline 05.04.2009 16:12

В C# атрибуты можно использовать с перечислениями. Хорошим примером этого шаблона программирования с подробным описанием является здесь (Codeproject).

public enum Planet
{
   [PlanetAttr(3.303e+23, 2.4397e6)]
   Mercury,
   [PlanetAttr(4.869e+24, 6.0518e6)]
   Venus
} 

Редактировать: этот вопрос недавно был снова задан, и на него ответил Джон Скит: Что эквивалентно перечислению Java в C#?Частные внутренние классы в C# - почему они не используются чаще?

Изменить 2: см. принятый ответ, который очень блестяще расширяет этот подход!

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

Ogre Psalm33 14.09.2009 23:11

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

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

Ogre Psalm33 25.05.2011 17:39

Может быть, да. Но помимо интеллектуальной языковой интеграции «переключателя», остальные функции могут быть легко воспроизведены на C# или самой Java, как показано в приведенных выше примерах.

dmihailescu 04.06.2011 01:12

@dmihailescu "Значит, enum в Java намного сложнее, чем C#. Поэтому я отказался от Java ..."

Mukus 12.02.2015 07:22

В C# вы можете определить методы расширения в перечислениях, и это восполняет некоторые недостающие функции.

Вы можете определить Planet как перечисление, а также иметь методы расширения, эквивалентные surfaceGravity() и surfaceWeight().

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

using System;
using System.Reflection;

class PlanetAttr: Attribute
{
    internal PlanetAttr(double mass, double radius)
    {
        this.Mass = mass;
        this.Radius = radius;
    }
    public double Mass { get; private set; }
    public double Radius { get; private set; }
}

public static class Planets
{
    public static double GetSurfaceGravity(this Planet p)
    {
        PlanetAttr attr = GetAttr(p);
        return G * attr.Mass / (attr.Radius * attr.Radius);
    }

    public static double GetSurfaceWeight(this Planet p, double otherMass)
    {
        return otherMass * p.GetSurfaceGravity();
    }

    public const double G = 6.67300E-11;

    private static PlanetAttr GetAttr(Planet p)
    {
        return (PlanetAttr)Attribute.GetCustomAttribute(ForValue(p), typeof(PlanetAttr));
    }

    private static MemberInfo ForValue(Planet p)
    {
        return typeof(Planet).GetField(Enum.GetName(typeof(Planet), p));
    }

}

public enum Planet
{
    [PlanetAttr(3.303e+23, 2.4397e6)]  MERCURY,
    [PlanetAttr(4.869e+24, 6.0518e6)]  VENUS,
    [PlanetAttr(5.976e+24, 6.37814e6)] EARTH,
    [PlanetAttr(6.421e+23, 3.3972e6)]  MARS,
    [PlanetAttr(1.9e+27,   7.1492e7)]  JUPITER,
    [PlanetAttr(5.688e+26, 6.0268e7)]  SATURN,
    [PlanetAttr(8.686e+25, 2.5559e7)]  URANUS,
    [PlanetAttr(1.024e+26, 2.4746e7)]  NEPTUNE,
    [PlanetAttr(1.27e+22,  1.137e6)]   PLUTO
}

Думаю, за это нужно проголосовать больше. Это ближе к тому, как работают перечисления в Java. Я могу сделать что-то вроде Planet.MERCURY.GetSurfaceGravity () <- Обратите внимание на метод расширения в Enum!

thenonhacker 25.05.2011 14:53

Определенно да. Методы расширения в Enums (черт возьми, методы расширения в целом) - отличное дополнение к C#.

KeithS 18.10.2011 18:11

@AllonGuralnek Спасибо. Однако не все согласятся насчет метаданных. См. Комментарий MattDavey к связанный вопрос codereview.stackexchange.

finnw 21.10.2011 20:54

@finnw: Я никогда не слышал о сайте Code Review SE - спасибо за это! Я продолжаю это обсуждение там (хотя, возможно, он заслуживает отдельного вопроса здесь, о Программистах).

Allon Guralnek 21.10.2011 21:29

это отличное решение - кто-нибудь тестировал производительность этого? например сколько времени требуется для доступа к атрибуту по сравнению с доступом к свойству класса.

Simon Meyer 29.02.2016 16:22

@SimonMeyer Конечно, вызывать Reflection непрактично, даже если оно кэшировано во время выполнения, с точки зрения производительности. Ответ высок, потому что он правильно ссылается на Методы расширения ENUM, как на ближайший аналог JAVA в C#.

Lorenz Lo Sauer 24.08.2016 06:06

Я подозреваю, что перечисления в C# - это просто константы, внутренние по отношению к CLR, но не знакомые с ними. Я декомпилировал некоторые классы на Java и могу сказать, что вы хотите, чтобы Enums были после преобразования.

Java делает что-то подлое. Он рассматривает класс enum как нормальный класс и, насколько я могу понять, использует множество макросов при обращении к значениям перечисления. Если у вас есть оператор case в классе Java, который использует перечисления, он заменяет ссылки перечисления на целые числа. Если вам нужно перейти к строке, он создает массив строк, индексированных порядковым номером, который он использует в каждом классе. Подозреваю, что сэкономлю на боксе.

Если вы загрузите этот декомпилятор, вы увидите, как он создает свой класс и интегрирует его. Честно говоря, довольно увлекательно. Раньше я не использовал класс enum, потому что думал, что он раздут только для массива констант. Мне это нравится больше, чем ограниченный способ их использования на C#.

http://members.fortunecity.com/neshkov/dj.html - декомпилятор Java

Вот еще одна интересная идея, которая учитывает настраиваемое поведение, доступное в Java. Я придумал следующий базовый класс Enumeration:

public abstract class Enumeration<T>
    where T : Enumeration<T>
{   
    protected static int nextOrdinal = 0;

    protected static readonly Dictionary<int, Enumeration<T>> byOrdinal = new Dictionary<int, Enumeration<T>>();
    protected static readonly Dictionary<string, Enumeration<T>> byName = new Dictionary<string, Enumeration<T>>();

    protected readonly string name;
    protected readonly int ordinal;

    protected Enumeration(string name)
        : this (name, nextOrdinal)
    {
    }

    protected Enumeration(string name, int ordinal)
    {
        this.name = name;
        this.ordinal = ordinal;
        nextOrdinal = ordinal + 1;
        byOrdinal.Add(ordinal, this);
        byName.Add(name, this);
    }

    public override string ToString()
    {
        return name;
    }

    public string Name 
    {
        get { return name; }
    }

    public static explicit operator int(Enumeration<T> obj)
    {
        return obj.ordinal;
    }

    public int Ordinal
    {
        get { return ordinal; }
    }
}

В основном у него есть параметр типа, поэтому порядковый номер будет правильно работать в разных производных перечислениях. Пример Operator Джона Скита из его ответа на другой вопрос (http://stackoverflow.com/questions/1376312/whats-the-equivalent-of-javas-enum-in-c) становится таким:

public class Operator : Enumeration<Operator>
{
    public static readonly Operator Plus = new Operator("Plus", (x, y) => x + y);
    public static readonly Operator Minus =  new Operator("Minus", (x, y) => x - y);
    public static readonly Operator Times =  new Operator("Times", (x, y) => x * y);
    public static readonly Operator Divide = new Operator("Divide", (x, y) => x / y);

    private readonly Func<int, int, int> op;

    // Prevent other top-level types from instantiating
    private Operator(string name, Func<int, int, int> op)
        :base (name)
    {
        this.op = op;
    }

    public int Execute(int left, int right)
    {
        return op(left, right);
    }
}

Это дает несколько преимуществ.

  • Порядковая поддержка
  • Преобразование в string и int, что делает операторы переключения возможными
  • GetType () даст одинаковый результат для каждого значения производного типа Enumeration.
  • Статические методы из System.Enum могут быть добавлены к базовому классу Enumeration, чтобы обеспечить ту же функциональность.
//Review the sample enum below for a template on how to implement a JavaEnum.
//There is also an EnumSet implementation below.

public abstract class JavaEnum : IComparable {
    public static IEnumerable<JavaEnum> Values {
        get {
            throw new NotImplementedException("Enumeration missing");
        }
    }

    public readonly string Name;

    public JavaEnum(string name) {
        this.Name = name;
    }

    public override string ToString() {
        return base.ToString() + "." + Name.ToUpper();
    }

    public int CompareTo(object obj) {
        if (obj is JavaEnum) {
            return string.Compare(this.Name, ((JavaEnum)obj).Name);
        } else {
            throw new ArgumentException();
        }
    }


    //Dictionary values are of type SortedSet<T>
    private static Dictionary<Type, object> enumDictionary;
    public static SortedSet<T> RetrieveEnumValues<T>() where T : JavaEnum {
        if (enumDictionary == null) {
            enumDictionary = new Dictionary<Type, object>();
        }
        object enums;
        if (!enumDictionary.TryGetValue(typeof(T), out enums)) {
            enums = new SortedSet<T>();
            FieldInfo[] myFieldInfo = typeof(T).GetFields(BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.Public);
            foreach(FieldInfo f in myFieldInfo) {
                if (f.FieldType == typeof(T)) {
                    ((SortedSet<T>)enums).Add((T)f.GetValue(null));
                }
            }
            enumDictionary.Add(typeof(T), enums);
        }
        return (SortedSet<T>)enums;
    }
}


//Sample JavaEnum
public class SampleEnum : JavaEnum {
    //Enum values
    public static readonly SampleEnum A = new SampleEnum("A", 1);
    public static readonly SampleEnum B = new SampleEnum("B", 2);
    public static readonly SampleEnum C = new SampleEnum("C", 3);

    //Variables or Properties common to all enums of this type
    public int int1;
    public static int int2 = 4;
    public static readonly int int3 = 9;

    //The Values property must be replaced with a call to JavaEnum.generateEnumValues<MyEnumType>() to generate an IEnumerable set.
    public static new IEnumerable<SampleEnum> Values {
        get {
            foreach(var e in JavaEnum.RetrieveEnumValues<SampleEnum>()) {
                yield return e;
            }
            //If this enum should compose several enums, add them here
            //foreach(var e in ChildSampleEnum.Values) {
            //    yield return e;
            //}
        }
    }

    public SampleEnum(string name, int int1)
        : base(name) {
        this.int1 = int1;
    }
}


public class EnumSet<T> : SortedSet<T> where T : JavaEnum {
    // Creates an enum set containing all of the elements in the specified element type.
    public static EnumSet<T> AllOf(IEnumerable<T> values) {
        EnumSet<T> returnSet = new EnumSet<T>();
        foreach(T item in values) {
            returnSet.Add(item);
        }
        return returnSet;
    }

    // Creates an enum set with the same element type as the specified enum set, initially containing all the elements of this type that are not contained in the specified set.
    public static EnumSet<T> ComplementOf(IEnumerable<T> values, EnumSet<T> set) {
        EnumSet<T> returnSet = new EnumSet<T>();
        foreach(T item in values) {
            if (!set.Contains(item)) {
                returnSet.Add(item);
            }
        }
        return returnSet;
    }

    // Creates an enum set initially containing all of the elements in the range defined by the two specified endpoints.
    public static EnumSet<T> Range(IEnumerable<T> values, T from, T to) {
        EnumSet<T> returnSet = new EnumSet<T>();
        if (from == to) {
            returnSet.Add(from);
            return returnSet;
        }
        bool isFrom = false;
        foreach(T item in values) {
            if (isFrom) {
                returnSet.Add(item);
                if (item == to) {
                    return returnSet;
                }
            } else if (item == from) {
                isFrom = true;
                returnSet.Add(item);
            }
        }
        throw new ArgumentException();
    }

    // Creates an enum set initially containing the specified element(s).
    public static EnumSet<T> Of(params T[] setItems) {
        EnumSet<T> returnSet = new EnumSet<T>();
        foreach(T item in setItems) {
            returnSet.Add(item);
        }
        return returnSet;
    }

    // Creates an empty enum set with the specified element type.
    public static EnumSet<T> NoneOf() {
        return new EnumSet<T>();
    }

    // Returns a copy of the set passed in.
    public static EnumSet<T> CopyOf(EnumSet<T> set) {
        EnumSet<T> returnSet = new EnumSet<T>();
        returnSet.Add(set);
        return returnSet;
    }

    // Adds a set to an existing set.
    public void Add(EnumSet<T> enumSet) {
        foreach(T item in enumSet) {
            this.Add(item);
        }
    }

    // Removes a set from an existing set.
    public void Remove(EnumSet<T> enumSet) {
        foreach(T item in enumSet) {
            this.Remove(item);
        }
    }
}

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

public enum Planet
{
    MERCURY,
    VENUS
}

public class PlanetUtil
{
    private static readonly IDictionary<Planet, PlanetUtil> PLANETS = new Dictionary<Planet, PlanetUtil();

    static PlanetUtil()
    {
        PlanetUtil.PLANETS.Add(Planet.MERCURY, new PlanetUtil(3.303e+23, 2.4397e6));
        PlanetUtil.PLANETS.Add(Planet.VENUS, new PlanetUtil(4.869e+24, 6.0518e6));
    }

    public static PlanetUtil GetUtil(Planet planet)
    {
        return PlanetUtil.PLANETS[planet];
    }

    private readonly double radius;
    private readonly double mass;

    public PlanetUtil(double radius, double mass)
    {
        this.radius = radius;
        this.mass = mass;
    }

    // getter
}

мы только что сделали расширение enum для C# https://github.com/simonmau/enum_ext

Это просто реализация для typeafeenum, но она отлично работает, поэтому мы сделали пакет, которым можно поделиться - получайте удовольствие

public sealed class Weekday : TypeSafeNameEnum<Weekday, int>
{
    public static readonly Weekday Monday = new Weekday(1, "--Monday--");
    public static readonly Weekday Tuesday = new Weekday(2, "--Tuesday--");
    public static readonly Weekday Wednesday = new Weekday(3, "--Wednesday--");
    ....

    private Weekday(int id, string name) : base(id, name)
    {
    }
}

Другие вопросы по теме