C# vs Java Enum (for those new to C#)
我已经用Java编程了一段时间,刚刚投入到一个完全用C语言编写的项目中。我试图在C语言中加快速度,注意到我的新项目中的几个地方使用了EnnS,但是乍一看,C的枚举似乎比Java 1.5实现更简单。谁能列举C语言和Java枚举之间的区别,以及如何克服它们之间的差异呢?(我不想开始一场语言火焰战,我只想知道我在爪哇做的一些事情。)例如,有人能发布一个与太阳著名的行星枚举例子相对应的C吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | 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 ...] |
在C中,您可以在枚举上定义扩展方法,这弥补了一些缺失的功能。
可以将
我使用了Mikhail建议的自定义属性,但使用字典也可以实现相同的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 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 } |
clr中的枚举只是命名为常量。基础类型必须是整数。在Java中,枚举更像是一个类型的命名实例。该类型可能非常复杂,正如您的示例所示,它包含各种类型的多个字段。
要将示例移植到C,我只需将枚举更改为不可变类,并公开该类的静态只读实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | 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; } } |
在C中,属性可以与枚举一起使用。这里是这个编程模式的一个很好的例子,有详细的描述(codeproject)
1 2 3 4 5 6 7 | public enum Planet { [PlanetAttr(3.303e+23, 2.4397e6)] Mercury, [PlanetAttr(4.869e+24, 6.0518e6)] Venus } |
编辑:这个问题最近被再次问及乔恩SkiTe:在Java语言中,Java的枚举是什么等价物?C中的私有内部类-为什么它们不经常使用?
编辑2:看到公认的答案,它以一种非常出色的方式扩展了这种方法!
Java枚举实际上是完整的类,它可以有一个私有的构造函数和方法等,而C ^枚举只是一个整数。IMO Java的实现远远优越。
这个页面在学习C JavaCAMP的时候会对你有很大帮助。(链接指向有关枚举的差异(向上/向下滚动查看其他内容)
我想是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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(); } } |
或者将这些常量组合到上面的
这是另一个有趣的想法,它迎合了Java中可用的习惯行为。我提出了以下的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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; } } } |
它有一个类型参数,基本上就是为了使序数能够在不同的派生枚举中正常工作。乔恩·斯基特对另一个问题(http://stackoverflow.com/question s/1376312/whats-the-equivalent-of-javas-enum-in-c)的回答中的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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()将为派生枚举类型的每个值提供相同的结果。
- 可以将
System.Enum 中的静态方法添加到基本枚举类中,以允许相同的功能。
我怀疑C中的枚举只是clr内部的常量,但并不熟悉它们。我已经用Java解压缩了一些类,并且我可以告诉你,一旦Enums转换,就需要。
Java做了一些鬼鬼祟祟的事情。它将枚举类视为一个普通类,尽我所能,在引用枚举值时使用大量宏。如果在使用枚举的Java类中有一个case语句,那么它将替换枚举引用到整数。如果需要转到字符串,它将创建一个字符串数组,该数组由它在每个类中使用的序号索引。我怀疑是为了节省拳击的费用。
如果您下载这个解压程序,您将看到它如何创建它的类,并将其集成。说实话相当吸引人。我以前不使用枚举类,因为我认为它只对一个常量数组膨胀。我比你在C中使用它们的有限方式更喜欢它。
HTTP://MeNest.FraveCITI.COM/NESHKOV/DJ.HTML——Java反编译程序
Java枚举允许使用编译器生成的Valueof方法从名称中轻松地进行类型转换,即
1 2 | // Java Enum has generics smarts and allows this Planet p = Planet.valueOf("MERCURY"); |
C中的原始枚举的等效值更详细:
1 2 |
但是,如果沿着kent建议的路径走,可以在枚举类中轻松实现
JavaEnUM是以OO方式呈现枚举的语法糖。它们是在Java中扩展EnUM类的抽象类,每个枚举值都类似于EnUM类的静态最终公共实例实现。查看生成的类,对于具有10个值的枚举"foo",您将看到生成的"foo$1"到"foo$10"类。
不过,我不知道C,我只能推测该语言中的枚举更像C风格语言中的传统枚举。我从快速谷歌搜索中看到,它们可以保存多个值,因此它们可能以类似的方式实现,但比Java编译器允许的限制要大得多。
您还可以为每个枚举类型使用实用程序类,该类为每个枚举值保存具有高级数据的实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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 } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | //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); } } } |
Java中的枚举要比C.E.EnUM复杂得多,因此更强大。因为它只是另一种编译时语法的制糖,我想知道考虑到它在实际应用程序中的有限使用,是否真的值得包含这种语言。有时候,把东西从语言中排除比放弃包含一个小特性的压力更难。