I've been programming in Java for a while and just got thrown onto a project that's written entirely in C#. I'm trying to come up to speed in C#, and noticed enums used in several places in my new project, but at first glance, C#'s enums seem to be more simplistic than the Java 1.5+ implementation. Can anyone enumerate the differences between C# and Java enums, and how to overcome the differences? (I don't want to start a language flame war, I just want to know how to do some things in C# that I used to do in Java). For example, could someone post a C# counterpart to Sun's famous Planet enum example?
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 ...]
下面是另一个有趣的想法,它可以满足Java中可用的自定义行为。我提出了以下枚举基类:
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; }
}
}
它有一个类型参数,所以序数计数可以在不同的派生枚举中正常工作。Jon Skeet对上面另一个问题(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);
}
}
这带来了一些好处。
序数的支持
转换为字符串和int,这使得switch语句可行
GetType()将为派生的Enumeration类型的每个值提供相同的结果。
来自System的静态方法。枚举可以添加到基本枚举类以允许相同的功能。
在c#中,你可以在枚举上定义扩展方法,这弥补了一些缺失的功能。
您可以将Planet定义为枚举,也可以使用与surfaceGravity()和surfaceWeight()等效的扩展方法。
我已经按照Mikhail的建议使用了自定义属性,但是使用Dictionary也可以达到同样的效果。
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
}