C#vs Java Enum(适用于C#的新手)
我一直在Java编程一段时间,只是被扔到一个完全用C#编写的项目。 我试图在C#中加快速度,并注意到在我的新项目中有几个地方使用了枚举,但乍一看,C#的枚举似乎比Java 1.5+实现更简单。 任何人都可以枚举C#和Java枚举之间的区别,以及如何克服差异? (我不想开始一场语言的火焰战,我只想知道如何在C#中做一些我曾经在Java中做过的事)。 例如,有人可以发布一个与Sun的着名的Planet enum例子相对应的C#吗?
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 ...]
CLR中的枚举只是简单的命名常量。 基础types必须是整数。 在Java中,枚举更像是一个types的命名实例。 这种types可能相当复杂,正如您的示例所示,它包含多个不同types的字段。
要将示例移植到C#,我只需将枚举更改为不可变类,并公开该类的静态只读实例:
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; } } }
在C#中,你可以在枚举上定义扩展方法 ,这就弥补了一些缺失的function。
您可以将Planet
定义为枚举,并且也可以使用等效于surfaceGravity()
和surfaceWeight()
扩展方法。
我已经使用Mikhailbuild议的自定义属性,但同样可以使用字典。
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 }
在C#中,属性可以和枚举一起使用。 这个编程模式的详细描述的好例子就在这里 (Codeproject)
public enum Planet { [PlanetAttr(3.303e+23, 2.4397e6)] Mercury, [PlanetAttr(4.869e+24, 6.0518e6)] Venus }
编辑:这个问题最近再次被问及由Jon Skeet回答: 在C#中Java的枚举相当于什么? C#中的私有内部类 – 为什么不经常使用?
编辑2:看到接受的答案 ,以非常辉煌的方式扩展了这种方法!
Java枚举实际上是完整的类,可以有一个私人的构造函数和方法等,而C#枚举只是命名整数。 国际海事组织Java的实施是远远优于。
从Java阵营学习c#的时候,这个页面应该会帮助你很多。 (链接指向关于枚举的差异(向上/向下滚动其他事物)
像这样的东西我想:
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
类中
Java枚举是以OO方式提供枚举的语法糖。 它们是在Java中扩展Enum类的抽象类,每个枚举值就像是一个枚举类的静态最终公共实例实现。 查看生成的类,对于具有10个值的枚举“Foo”,您将通过生成的“Foo $ 10”类看到“Foo $ 1”。
我不知道C#,但我只能推测,这种语言的枚举更像是一种传统的C语言风格的枚举。 我从一个快速的Googlesearch中看到,他们可以拥有多个值,所以他们可能以类似的方式实现,但是比Java编译器允许的限制要多得多。
Java枚举允许使用编译器生成的valueOf方法从名称轻松进行types安全转换,即
// 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");
但是,如果你沿着Kent的路线走,你可以很容易地在你的enum类中实现一个ValueOf
方法。
我怀疑C#中的枚举只是CLR内部的常量,但不是那些熟悉它们的常量。 我已经反编译Java中的一些类,我可以告诉你想Enums是一旦你转换。
Java做鬼鬼祟祟的事情。 它将枚举类视为一个普通的类,尽可能的接近引用枚举值时使用大量的macros。 如果在使用枚举的Java类中有一个case语句,它会将枚举引用replace为整数。 如果您需要转到string,它会创build一个由每个类中使用的序号索引的string数组。 我怀疑节省拳击。
如果你下载这个反编译器,你会看到它是如何创build一个集成它的类。 说实话,真是迷人。 我曾经不使用枚举类,因为我认为这只是一个常数的数组膨胀。 我喜欢它比在C#中使用它们的方式有限。
这是另一个有趣的想法,它迎合了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; } } }
它基本上只有一个types参数,所以序数会在不同的派生枚举中正常工作。 Jon Skeet的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
,这使得switch语句可行 - GetType()将为派生的Enumerationtypes的每个值提供相同的结果。
-
System.Enum
的静态方法可以添加到基本的Enumeration类中,以实现相同的function。
Java中的枚举比C#枚举复杂得多,因此function更强大。 既然这只是另一个编译时语法糖,我想知道是否真的值得在现实生活应用程序中使用有限的语言。 有时候,把语言放在语言之外比放弃包含一个小function的压力要困难得多。
//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); } } }
您也可以为每个枚举types使用一个实用程序类,该types为每个枚举值保存一个具有高级数据的实例。
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 }