C # vs Java Enum (dla nowych w C #)

182

Od jakiegoś czasu programuję w Javie i właśnie zostałem wrzucony do projektu napisanego całkowicie w języku C #. Próbuję przyspieszyć w C # i zauważyłem wyliczenia używane w kilku miejscach w moim nowym projekcie, ale na pierwszy rzut oka wyliczenia C # wydają się być bardziej uproszczone niż implementacja Java 1.5+. Czy ktoś może wymienić różnice między wyliczeniami C # i Java oraz jak przezwyciężyć różnice? (Nie chcę rozpoczynać wojny z płomieniami językowymi, chcę tylko wiedzieć, jak zrobić pewne rzeczy w języku C #, które robiłem wcześniej w Javie). Na przykład, czy ktoś może opublikować odpowiednik C # do słynnego przykładu enum Planet Słońca?

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 ...]
Ogre Psalm 33
źródło
1
@ycomp Nie mogę tego przypisać. Pochodzi z firmy Sun (obecnie Oracle): docs.oracle.com/javase/tutorial/java/javaOO/enum.html
Ogre Psalm33

Odpowiedzi:

210

Wyliczenia w CLR są po prostu nazwane stałymi. Typ bazowy musi być integralny. W Javie wyliczenie jest bardziej jak nazwane wystąpienie typu. Ten typ może być dość złożony i - jak pokazuje twój przykład - zawierać wiele pól różnych typów.

Aby przenieść przykład na C #, po prostu zmieniłem wyliczenie na niezmienną klasę i ujawnięłem statyczne instancje tylko do odczytu dla tej klasy:

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;
    }
}
Kent Boogaart
źródło
4
Jest to rodzaj bezpiecznego wyliczania, który my, biedni ludzie zmuszeni do korzystania z Java 1.4 i niższych, musimy wdrożyć ... Wyliczenia Java 5 są prawdopodobnie najlepszą cechą Java 5+, zwłaszcza że można ich używać w instrukcjach przełączników.
MetroidFan2002
9
@Chris: tylko wyliczenia flag powinny być pluralizowane. To znaczy, wyliczenia, których członkowie są łączeni przy użyciu | operator.
Kent Boogaart,
5
@Mladen: To zależy całkowicie od kontekstu. Wyliczenie planet może idealnie pasować do gry, która zapewnia dostęp do ograniczonej liczby planet. Zmiany w kodzie mogą być dokładnie tym, czego chcesz, jeśli do gry zostanie dodana nowa planeta.
Kent Boogaart,
3
@Richie_W można iterować wyliczenia za pomocą właściwości Values.
Jonathan
23
Wow ... To prawie niewiarygodne, aby zobaczyć coś zaimplementowanego w bardziej szczegółowy sposób w C # niż w Javie
Sune Rasmussen
218

W języku C # można zdefiniować metody rozszerzeń w wyliczeniach, co uzupełnia niektóre brakujące funkcje.

Możesz zdefiniować Planetjako wyliczenie, a także mieć metody rozszerzenia równoważne do surfaceGravity()i surfaceWeight().

Użyłem niestandardowych atrybutów zgodnie z sugestią Michaiła , ale to samo można osiągnąć za pomocą słownika.

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
}
finnw
źródło
20
Myślę, że należy to bardziej zagłosować. Jest bliżej tego, jak działa enum w Javie. Mogę zrobić coś takiego jak Planet.MERCURY.GetSurfaceGravity () <- Zwróć uwagę na metodę rozszerzenia Enum!
następnieonakrowiec
2
Zdecydowanie tak. Metody rozszerzeń na Enums (heck, ogólnie metody rozszerzeń) są doskonałym dodatkiem do C #.
KeithS
3
@AllonGuralnek Thanks. Jednak nie wszyscy zgodzą się co do metadanych. Zobacz komentarz MattDavey'a na powiązane pytanie codereview.stackexchange .
finnw
@finnw: Nigdy nie słyszałem o witrynie Code Review SE - dzięki za to! Kontynuuję tę dyskusję tam (chociaż może zasługiwać na swoje własne pytanie tutaj w sprawie programistów).
Allon Guralnek
to świetne rozwiązanie - czy ktoś przetestował jego wydajność? np. ile czasu zajmuje dostęp do atrybutu, w porównaniu do dostępu do właściwości klasy.
Simon Meyer
35

W języku C # atrybuty mogą być używane z wyliczeniami. Dobry przykład tego schematu programowania ze szczegółowym opisem znajduje się tutaj (Projekt kodowy)

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

Edycja: to pytanie zostało ostatnio zadane ponownie i odpowiedział Jon Skeet: Jaki jest odpowiednik wyliczenia Java w C #? Prywatne klasy wewnętrzne w języku C # - dlaczego nie są używane częściej?

Edycja 2: zobacz przyjętą odpowiedź, która w bardzo błyskotliwy sposób rozszerza to podejście!

Michaił
źródło
1
Miły! To wydaje się tylko trochę niezgrabne, ale poza tym jest bardzo akceptowalną metodą dodawania dodatkowych danych do wyliczenia. Jestem szczerze zdziwiony, że tak długo ktoś wspominał o tym świetnym rozwiązaniu!
Ogre Psalm33
13

Wyliczenia Java są w rzeczywistości pełnymi klasami, które mogą mieć prywatnego konstruktora i metody itp., Natomiast wyliczenia C # to tylko liczby całkowite. Implementacja IMO Java jest znacznie lepsza.

Ta strona powinna ci bardzo pomóc w nauce języka c # pochodzącego z obozu java. (Link wskazuje na różnice w wyliczeniach (przewiń w górę / w dół, aby zobaczyć inne rzeczy)

Richard Walton
źródło
1
Podczas gdy twój link daje ciekawy, obszerny przegląd podobieństw i różnic między C # a Javą, w tekście jest wiele błędów (np. Błędnie stwierdza, że ​​Java chroniona jest równa wewnętrznemu C #, podczas gdy powinna być chroniona wewnętrznie). Więc nie bierz wszystkiego tam za pewnik :)
mafu
1
Nie powiedziałbym, że wyliczenia Java są lepsze, nawet jeśli jestem fanem Javy. C # obsługuje łatwą deklarację liczb całkowitych, taką jak ta, FOO = 0która jest łatwiejsza w użyciu w narzędziach ORM (nie ordinal()wymaga użycia podatnego na błędy ). Dalsze C # obsługuje wyliczenia bitowe, które często są bardzo przydatne, szczególnie w połączeniu z EntityFramework. Java powinna rozszerzyć swoje wyliczenia, aby umożliwić ich powiązanie również z liczbami całkowitymi. Wtedy byliby lepsi :)
djmj
4

Myślę, że coś takiego:

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

Lub połącz stałe w Planetklasę jak wyżej

Chris S.
źródło
8
Niezupełnie - konstruktor Planety powinien być prywatny; częścią tego wyliczenia jest to, że są one stałym zestawem wartości. Wartości byłyby wówczas również zdefiniowane w klasie Planet.
Jon Skeet
Jeszcze nie. 1) brakuje modułu wyliczającego :) 2) wyliczenia nigdy nie powinny być zmienne. No i na koniec, twój kod prosi o jedną pojedynczą klasę (szczególnie gdy masz prywatnego konstruktora)
nawfal
3

Oto kolejny interesujący pomysł, który uwzględnia niestandardowe zachowanie dostępne w Javie. Wymyśliłem następującą Enumerationklasę podstawową:

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

Ma parametr typu w zasadzie tylko po to, aby liczba porządkowa działała poprawnie w różnych wyliczeniach pochodnych. OperatorPrzykład Jona Skeeta z jego odpowiedzi na inne pytanie (http://stackoverflow.com/questions/1376312/whats-the-equivalent-of-javas-enum-in-c) powyżej brzmi:

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

Daje to kilka zalet.

  • Wsparcie porządkowe
  • Konwersja na stringi, intktóra umożliwia instrukcje zamiany jest możliwa
  • Funkcja GetType () da taki sam wynik dla każdej wartości pochodnego typu wyliczenia.
  • Metody statyczne z System.Enummożna dodać do podstawowej klasy wyliczania, aby umożliwić tę samą funkcjonalność.
Andrew Cooper
źródło
3

właśnie stworzyliśmy rozszerzenie enum dla c # https://github.com/simonmau/enum_ext

To tylko implementacja dla typeafeenum, ale działa świetnie, dlatego stworzyliśmy pakiet do dzielenia się - baw się dobrze

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)
    {
    }
}
simonmau
źródło
2

Wyliczenie Java jest cukrem syntaktycznym służącym do przedstawiania wyliczeń w sposób OO. Są to abstrakcyjne klasy rozszerzające klasę Enum w Javie, a każda wartość enum jest jak statyczna końcowa publiczna implementacja klasy enum. Spójrz na wygenerowane klasy, a dla wyliczenia „Foo” z 10 wartościami zobaczysz wygenerowane klasy „Foo $ 1” do „Foo $ 10”.

Nie znam jednak C #, mogę jedynie spekulować, że wyliczenie w tym języku jest bardziej podobne do tradycyjnego wyliczenia w językach w stylu C. Widzę z szybkiego wyszukiwania Google, że mogą one przechowywać wiele wartości, więc prawdopodobnie są one implementowane w podobny sposób, ale z dużo większymi ograniczeniami niż pozwala na to kompilator Java.

JeeBee
źródło
3
Cóż, czy nie wszystko w Javie i C # dotyczy syntaktycznego cukru dla kodów bajtowych JVM lub CLR? :) Po prostu
mówię
2

Wyliczenia Java umożliwiają łatwe konwersje typów z nazwy za pomocą generowanej przez kompilator metody valueOf, tj

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

Odpowiednik surowego wyliczenia w języku C # jest bardziej szczegółowy:

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

Jeśli jednak pójdziesz drogą Kentem, możesz łatwo wdrożyć ValueOfmetodę w klasie enum.

serg10
źródło
Przykład Java wykorzystuje syntetyczną metodę generowaną przez kompilator - nie ma to nic wspólnego z ogólnymi. Enum ma ogólną metodę valueOf, ale używa ona generycznych klas, a nie Enum.
Tom Hawtin - tackline
2

Podejrzewam, że wyliczenia w języku C # są tylko stałymi wewnętrznymi dla CLR, ale nie są tak dobrze z nimi zaznajomione. Zdekompilowałem niektóre klasy w Javie i mogę powiedzieć, że chcesz, aby Enums były konwertowane.

Java robi coś podstępnego. Traktuje klasę wyliczania jako normalną klasę, która, tak blisko, jak to możliwe, używa wielu makr podczas odwoływania się do wartości wyliczania. Jeśli masz instrukcję case w klasie Java, która używa wyliczeń, zastępuje odniesienia wyliczeń do liczb całkowitych. Jeśli potrzebujesz przejść do łańcucha, tworzy on tablicę łańcuchów indeksowanych przez porządek, którego używa w każdej klasie. Podejrzewam, że zaoszczędzę na boksie.

Jeśli pobierzesz ten dekompilator, zobaczysz, jak tworzy on swoją klasę i integruje go. Szczerze mówiąc, fascynujące. Kiedyś nie korzystałem z klasy enum, ponieważ myślałem, że jest ona rozdęta tylko dla szeregu stałych. Podoba mi się to bardziej niż ograniczony sposób używania ich w języku C #.

http://members.fortunecity.com/neshkov/dj.html - Dekompilator Java

Paul Bruner
źródło
0

Wyliczanie w Javie jest znacznie bardziej złożone niż wyliczanie w języku C #, a zatem silniejsze. Ponieważ jest to tylko kolejny cukier syntaktyczny w czasie kompilacji, zastanawiam się, czy naprawdę warto było włączyć ten język, biorąc pod uwagę jego ograniczone użycie w rzeczywistych aplikacjach. Czasami trudniej jest trzymać rzeczy z dala od języka niż poddawać się presji, by dołączyć drobne funkcje.

dmihailescu
źródło
2
Z szacunkiem się nie zgadzam. Wyliczenia Java 1.5 są potężną funkcją językową, z której wielokrotnie korzystałem, aby zaimplementować bardziej zorientowane na OO rozwiązanie problemu dotyczącego dyskretnego zestawu stałych nazwanych elementów.
Ogre Psalm 33
1
Być może ty to zrobiłeś. Ale poza inteligentną integracją językową „przełącznika” resztę funkcjonalności można łatwo powielić w języku C # lub w samej Javie, jak pokazano w powyższych przykładach.
dmihailescu
@dmihailescu „Zatem wyliczanie w Javie jest znacznie bardziej skomplikowane niż w języku C #. Więc porzuciłem Javę ...”
Mukus
0
//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);
        }
    }
}
Jim
źródło
0

Możesz także użyć klasy użyteczności dla każdego typu wyliczenia, która przechowuje instancję z zaawansowanymi danymi dla każdej wartości wyliczenia.

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
}
djmj
źródło