C # vs Java Enum (para aquellos nuevos en C #)

182

He estado programando en Java por un tiempo y acabo de lanzarme a un proyecto que está escrito completamente en C #. Estoy tratando de acelerar en C #, y noté que las enumeraciones se utilizan en varios lugares en mi nuevo proyecto, pero a primera vista, las enumeraciones de C # parecen ser más simplistas que la implementación de Java 1.5+. ¿Alguien puede enumerar las diferencias entre C # y las enumeraciones de Java, y cómo superar las diferencias? (No quiero comenzar una guerra de llamas de lenguaje, solo quiero saber cómo hacer algunas cosas en C # que solía hacer en Java). Por ejemplo, ¿alguien podría publicar una contraparte de C # en el famoso ejemplo de enum Planet de Sun?

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 ...]
Salmo de ogro33
fuente
1
@ycomp No puedo tomar crédito por eso. Proviene de Sun (ahora Oracle): docs.oracle.com/javase/tutorial/java/javaOO/enum.html
Ogre Psalm33

Respuestas:

210

Las enumeraciones en el CLR se denominan simplemente constantes. El tipo subyacente debe ser integral. En Java, una enumeración es más como una instancia con nombre de un tipo. Ese tipo puede ser bastante complejo y, como muestra su ejemplo, contener múltiples campos de varios tipos.

Para transferir el ejemplo a C #, simplemente cambiaría la enumeración a una clase inmutable y expondría instancias de solo lectura estática de esa clase:

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
fuente
44
Es este tipo de enumeración de tipo seguro que nosotros, los pobres obligados a usar Java 1.4 y versiones posteriores, tenemos que implementar ... Las enumeraciones de Java 5 son quizás la mejor característica de Java 5+, especialmente porque pueden usarse en declaraciones de cambio.
MetroidFan2002
9
@Chris: solo las enumeraciones de banderas deben estar pluralizadas. Es decir, enumeraciones cuyos miembros se combinan utilizando el | operador.
Kent Boogaart
55
@Mladen: Depende completamente del contexto. Una enumeración de planetas puede ser perfectamente adecuada para un juego que proporciona acceso a un número limitado de planetas. Los cambios en el código pueden ser exactamente lo que desea si se agrega un nuevo planeta al juego.
Kent Boogaart el
3
@Richie_W puede iterar sobre las enumeraciones utilizando la propiedad Valores.
Jonathan
23
Wow ... Es casi increíble ver algo implementado de una manera más detallada en C # que en Java
Sune Rasmussen
218

En C # puede definir métodos de extensión en enumeraciones, y esto compensa algunas de las funciones faltantes.

Puede definir Planetcomo una enumeración y también tener métodos de extensión equivalentes a surfaceGravity()y surfaceWeight().

He usado atributos personalizados como sugiere Mikhail , pero lo mismo podría lograrse usando un Diccionario.

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
fuente
20
Creo que esto debería votarse más. Está más cerca de cómo funcionan las enumeraciones en Java. Puedo hacer algo como Planet.MERCURY.GetSurfaceGravity () <- ¡Tenga en cuenta el método de extensión en un Enum!
thenonhacker
2
Definitivamente si. Los métodos de extensión en Enums (heck, métodos de extensión en general) son una gran adición a C #.
KeithS
3
@AllonGuralnek Gracias. Sin embargo, no todos estarían de acuerdo con los metadatos. Vea el comentario de MattDavey sobre una pregunta relacionada codereview.stackexchange .
finnw
@finnw: Nunca he oído hablar del sitio de Code Review SE, ¡gracias por eso! Continué esta discusión allí (aunque podría merecer su propia pregunta aquí en Programadores).
Allon Guralnek
Esta es una gran solución. ¿Alguien ha probado el rendimiento de esto? por ejemplo, cuánto tiempo lleva acceder al atributo, en comparación con el acceso a una propiedad de una clase.
Simon Meyer
35

En C # los atributos se pueden usar con enumeraciones. Un buen ejemplo de este patrón de programación con una descripción detallada está aquí (Codeproject)

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

Editar: esta pregunta se volvió a hacer recientemente y Jon Skeet respondió: ¿Cuál es el equivalente de la enumeración de Java en C #? Clases internas privadas en C #: ¿por qué no se usan con más frecuencia?

Edición 2: ¡ vea la respuesta aceptada que extiende este enfoque de una manera muy brillante!

Mikhail
fuente
1
¡Agradable! Esto se siente solo un poco torpe, pero por lo demás es un método muy aceptable para agregar datos adicionales a una enumeración. ¡Estoy francamente sorprendido de que alguien haya tardado tanto en mencionar esta gran solución!
Ogre Psalm33
13

Las enumeraciones de Java son en realidad clases completas que pueden tener un constructor privado y métodos, etc., mientras que las enumeraciones de C # son simplemente enteros. La implementación de IMO Java es muy superior.

Esta página debería ayudarte mucho mientras aprendes c # proveniente de un campamento de Java. (El enlace apunta a las diferencias sobre las enumeraciones (desplácese hacia arriba / abajo para otras cosas)

Richard Walton
fuente
1
Si bien su enlace brinda una visión general amplia e interesante de las similitudes y diferencias entre C # y Java, hay muchos errores en el texto (por ejemplo, afirma erróneamente que Java protegido es igual a C # interno mientras que debería estar protegido internamente). No es que no todo lo que hay por sentado :)
Mafu
1
No diría que las enumeraciones de Java son superiores incluso si soy fanático de Java. C # admite una declaración de enteros fácil, como la FOO = 0que es más fácil de usar en las herramientas ORM (no es ordinal()necesario el uso propenso a errores ). Además, C # admite enumeraciones bit a bit que a menudo son muy útiles, especialmente en combinación con EntityFramework. Java debería extender sus enumeraciones para permitir que también se unan a enteros. Entonces serían superiores :)
djmj
4

Algo como esto, creo:

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

O combine las constantes en la Planetclase como arriba

Chris S
fuente
8
No del todo: el constructor de Planet debería ser privado; Parte del punto de las enumeraciones es que son un conjunto fijo de valores. Los valores se definirían también en la clase Planeta también.
Jon Skeet el
Aún no. 1) falta el enumerador :) 2) las enumeraciones nunca deben ser mutables. Ah, y por último, su código pide una sola clase (especialmente cuando tiene un constructor privado)
nawfal
3

Aquí hay otra idea interesante que satisface el comportamiento personalizado disponible en Java. Se me ocurrió la siguiente Enumerationclase base:

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

Tiene un parámetro de tipo básicamente solo para que el recuento ordinal funcione correctamente en diferentes enumeraciones derivadas. El Operatorejemplo de Jon Skeet de su respuesta a otra pregunta (http://stackoverflow.com/questions/1376312/whats-the-equivalent-of-javas-enum-in-c) se convierte en:

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

Esto le da algunas ventajas.

  • Soporte ordinal
  • Conversión a stringy intque hace factibles las declaraciones de cambio
  • GetType () dará el mismo resultado para cada uno de los valores de un tipo de enumeración derivado.
  • Los métodos estáticos de System.Enumse pueden agregar a la clase de enumeración base para permitir la misma funcionalidad.
Andrew Cooper
fuente
3

acabamos de hacer una extensión enum para c # https://github.com/simonmau/enum_ext

Es solo una implementación para el typesafeenum, pero funciona muy bien, así que creamos un paquete para compartir, diviértete con él

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
fuente
2

Una enumeración de Java es azúcar sintáctica para presentar enumeraciones de una manera OO. Son clases abstractas que extienden la clase Enum en Java, y cada valor enum es como una implementación estática de instancia pública final de la clase enum. Mire las clases generadas, y para una enumeración "Foo" con 10 valores, verá las clases "Foo $ 1" a "Foo $ 10" generadas.

Sin embargo, no sé C #, solo puedo especular que una enumeración en ese lenguaje es más como una enumeración tradicional en lenguajes de estilo C. Sin embargo, veo en una búsqueda rápida en Google que pueden contener múltiples valores, por lo que probablemente se implementen de manera similar, pero con muchas más restricciones de las que permite el compilador de Java.

JeeBee
fuente
3
Bueno, ¿no es todo en Java y C # todo sobre el azúcar sintáctico para los códigos de bytes JVM o CLR? :) Solo
digo
2

Las enumeraciones de Java permiten conversiones fáciles de escribir a partir del nombre utilizando el método valueOf generado por el compilador, es decir

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

El equivalente para una enumeración en bruto en C # es más detallado:

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

Sin embargo, si sigue la ruta sugerida por Kent, puede implementar fácilmente un ValueOfmétodo en su clase de enumeración.

serg10
fuente
El ejemplo de Java está utilizando un método sintético generado por el compilador, nada que ver con los genéricos. Enum tiene un método valueOf genérico, pero que utiliza los genéricos de Class y no los de Enum.
Tom Hawtin - tackline 05 de
2

Sospecho que las enumeraciones en C # son solo constantes internas del CLR, pero no están tan familiarizadas con ellas. He descompilado algunas clases en Java y puedo decirte que quieres Enums una vez que te conviertes.

Java hace algo astuto. Trata la clase enum como una clase normal con, por lo que puedo imaginar, usando muchas macros al hacer referencia a los valores enum. Si tiene una declaración de caso en una clase Java que usa enumeraciones, reemplaza las referencias de enumeración a enteros. Si necesita ir a una cadena, crea una matriz de cadenas indexadas por un ordinal que usa en cada clase. Sospecho que ahorraré en el boxeo.

Si descarga este descompilador, verá cómo crea su clase y la integra. Más bien fascinante para ser honesto. No solía usar la clase enum porque pensaba que era hinchada solo por una serie de constantes. Me gusta más que la forma limitada en que puedes usarlos en C #.

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

Paul Bruner
fuente
0

La enumeración en Java es mucho más compleja que la enumeración de C # y, por lo tanto, es más potente. Dado que es solo otro azúcar sintáctico en tiempo de compilación, me pregunto si realmente valió la pena incluir el lenguaje dado su uso limitado en aplicaciones de la vida real. A veces es más difícil mantener las cosas fuera del lenguaje que renunciar a la presión de incluir una característica menor.

dmihailescu
fuente
2
Respetuosamente no estoy de acuerdo. Las enumeraciones de Java 1.5 son una potente característica de lenguaje que he usado varias veces para implementar una solución más centrada en OO a un problema relacionado con un conjunto discreto de elementos con nombre constante.
Ogre Psalm33
1
Puede ser que lo hiciste. Pero más allá de la integración inteligente del lenguaje 'switch', el resto de la funcionalidad se puede replicar fácilmente en C # o en Java, como lo muestran los ejemplos anteriores.
dmihailescu
@dmihailescu "Entonces, la enumeración en Java es mucho más compleja que C #. Así que dejé caer Java ..."
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
fuente
0

También podría usar una clase de utilidad para cada tipo de enumeración que contiene una instancia con datos avanzados para cada valor de enumeración.

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
fuente