code - C#vs Java Enum(para los nuevos en C#)
enum#c (12)
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 clase Planet
como se indica arriba
He estado programando en Java por un tiempo y me han lanzado a un proyecto que está escrito completamente en C #. Estoy tratando de ponerme al día en C #, y me di cuenta de las enumeraciones usadas en varios lugares en mi nuevo proyecto, pero a primera vista, las enumeraciones de C # parecen ser más simples 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 llama de idioma, solo quiero saber cómo hacer algunas cosas en C # que solía hacer en Java). Por ejemplo, ¿podría alguien publicar una contraparte de C # en el famoso ejemplo de Sun Planet enum?
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 ...]
Aquí hay otra idea interesante que atiende el comportamiento personalizado disponible en Java. Se me ocurrió la siguiente clase básica de 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; }
}
}
Básicamente, tiene un parámetro de tipo simplemente para que el recuento ordinal funcione correctamente en diferentes enumeraciones derivadas. El ejemplo del Operator
Jon Skeet de su respuesta a otra pregunta (http://.com/questions/1376312/whats-the-equivalent-of-javas-enum-in-c) de arriba 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 ofrece algunas ventajas.
- Soporte ordinal
- Conversión a
string
eint
que hace factibles las declaraciones de cambio - GetType () dará el mismo resultado para cada uno de los valores de un tipo de enumeración derivada.
- Los métodos estáticos de
System.Enum
se pueden agregar a la clase base Enumeration para permitir la misma funcionalidad.
En C # los atributos se pueden usar con enums. Buen ejemplo de este patrón de programación con descripción detallada here (Codeproject)
public enum Planet
{
[PlanetAttr(3.303e+23, 2.4397e6)]
Mercury,
[PlanetAttr(4.869e+24, 6.0518e6)]
Venus
}
Editar: esta pregunta ha sido nuevamente formulada y respondida por Jon Skeet: ¿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 amplía este enfoque de una manera muy brillante.
En C # puede definir métodos de extensión en enumeraciones, y esto compensa algunas de las funcionalidades faltantes.
Puede definir Planet
como una enumeración y también tiene métodos de extensión equivalentes a surfaceGravity()
y surfaceWeight()
.
He utilizado atributos personalizados como sugiere Mikhail , pero lo mismo podría lograrse utilizando 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
}
La enumeración en Java es mucho más compleja que Cum enum y, por lo tanto, más poderosa. Dado que es simplemente otro azúcar sintáctico en tiempo de compilación, me pregunto si realmente valió la pena haber incluido el lenguaje dado su uso limitado en aplicaciones de la vida real. A veces es más difícil mantener las cosas fuera del idioma que ceder a la presión de incluir una característica menor.
Las enumeraciones de Java permiten conversiones fáciles de usar 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 sin procesar en C # es más detallado:
// C# enum - bit of hoop jumping required
Planet p = (Planet)Enum.Parse(typeof(Planet), "MERCURY");
Sin embargo, si baja por la ruta sugerida por Kent, puede implementar fácilmente un método ValueOf
en su clase enum.
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 números enteros. La implementación de IMO Java es muy superior.
Esta página debería ayudarte mucho mientras aprendes c # de un campamento de java. (El enlace apunta a las diferencias sobre enumeraciones (desplazarse hacia arriba / abajo para otras cosas)
Las enumeraciones en el CLR son simplemente constantes. El tipo subyacente debe ser integral. En Java, una enumeración es más como una instancia nombrada de un tipo. Ese tipo puede ser bastante complejo y, como muestra tu ejemplo, contener múltiples campos de varios tipos.
Para convertir el ejemplo en C #, simplemente cambiaría la enumeración a una clase inmutable y expondría instancias de solo lectura estáticas de esa clase:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Planet pEarth = Planet.MERCURY;
double earthRadius = pEarth.Radius; // Just threw it in to show usage
double earthWeight = double.Parse("123");
double mass = earthWeight / pEarth.SurfaceGravity();
foreach (Planet p in Planet.Values)
Console.WriteLine("Your weight on {0} is {1}", p, 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;
}
}
private readonly string name;
private readonly double mass; // in kilograms
private readonly double radius; // in meters
Planet(string name, double mass, double radius)
{
this.name = name;
this.mass = mass;
this.radius = radius;
}
public string Name { get { return name; } }
public double Mass { get { return mass; } }
public double Radius { get { return radius; } }
// universal gravitational constant (m3 kg-1 s-2)
public const double G = 6.67300E-11;
public double SurfaceGravity()
{
return G * mass / (radius * radius);
}
public double SurfaceWeight(double otherMass)
{
return otherMass * SurfaceGravity();
}
public override string ToString()
{
return name;
}
}
}
Sospecho que las enumeraciones en C # son solo constantes internas de la CLR, pero no tan familiarizadas con ellas. He descompilado algunas clases en Java y puedo decirte que quieres Enumerar una vez que conviertas.
Java hace algo furtivo. Trata la clase enum como una clase normal con, lo más cerca que puedo imaginar, usando muchas macros al hacer referencia a los valores enum. Si tiene una declaración de caso en una clase de Java que utiliza enumeraciones, reemplaza las referencias enum a enteros. Si necesita ir a la cadena, crea una matriz de cadenas indexadas por un ordinal que usa en cada clase. Sospecho que ahorrar en el boxeo.
Si descarga este decompilador, verá cómo crea su clase y lo integra. Bastante fascinante para ser honesto. Solía no usar la clase enum porque pensé que era inflado 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 - decompilador de Java
También podría usar una clase de utilidad para cada tipo de enumeración que contenga una instancia con datos avanzados para cada valor enum.
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
}
Una enumeración Java es azúcar sintáctica para presentar enumeraciones de manera OO. Son clases abstractas que amplían la clase Enum en Java, y cada valor enum es como una implementación de instancia pública final estática de la clase enum. Mire las clases generadas, y para una enumeración "Foo" con 10 valores, verá que se generarán "Foo $ 1" a través de las clases "Foo $ 10".
No obstante, no sé C #, solo puedo especular que una enumeración en ese idioma es más como una enumeración tradicional en los 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.
//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);
}
}
}