C# 与 Java Enum(对于 C# 新手)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/469287/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 04:38:34  来源:igfitidea点击:

C# vs Java Enum (for those new to C#)

c#javaenumslanguage-comparisons

提问by Ogre Psalm33

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?

我已经用 Java 编程有一段时间了,刚刚被扔到一个完全用 C# 编写的项目中。我正在努力提高 C# 的速度,并注意到在我的新项目中的几个地方使用了枚举,但乍一看,C# 的枚举似乎比 Java 1.5+ 实现更简单。谁能列举 C# 和 Java 枚举之间的差异,以及如何克服这些差异?(我不想开始语言的火焰战争,我只想知道如何在 C# 中做一些我曾经在 Java 中做的事情)。例如,有人可以发布 Sun 著名的 Planet 枚举示例的 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 ...]

采纳答案by Kent Boogaart

Enumerations in the CLR are simply named constants. The underlying type must be integral. In Java an enumeration is more like a named instance of a type. That type can be quite complex and - as your example shows - contain multiple fields of various types.

CLR 中的枚举只是命名常量。基础类型必须是整数。在 Java 中,枚举更像是类型的命名实例。该类型可能非常复杂,并且 - 如您的示例所示 - 包含各种类型的多个字段。

To port the example to C# I would just change the enum to an immutable class and expose static readonly instances of that class:

要将示例移植到 C#,我只需将枚举更改为不可变类并公开该类的静态只读实例:

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

回答by JeeBee

A Java enum is syntactic sugar to present enumerations in an OO manner. They're abstract classes extending the Enum class in Java, and each enum value is like a static final public instance implementation of the enum class. Look at the generated classes, and for an enum "Foo" with 10 values, you'll see "Foo$1" through "Foo$10" classes generated.

Java 枚举是一种以面向对象的方式呈现枚举的语法糖。它们是扩展 Java 中 Enum 类的抽象类,每个 enum 值就像 enum 类的静态最终公共实例实现。查看生成的类,对于具有 10 个值的枚举“Foo”,您将看到生成的“Foo$1”到“Foo$10”类。

I don't know C# though, I can only speculate that an enum in that language is more like a traditional enum in C style languages. I see from a quick Google search that they can hold multiple values however, so they are probably implemented in a similar manner, but with far more restrictions than what the Java compiler allows.

虽然我不知道 C#,但我只能推测该语言中的枚举更像是 C 风格语言中的传统枚举。我从一个快速的谷歌搜索中看到,它们可以保存多个值,所以它们可能以类似的方式实现,但比 Java 编译器允许的限制要多得多。

回答by Richard Walton

Java enums are actually full classes which can have a private constructor and methods etc, whereas C# enums are just named integers. IMO Java's implementation is far superior.

Java 枚举实际上是完整的类,可以具有私有构造函数和方法等,而 C# 枚举只是命名整数。IMO Java 的实现要好得多。

This page should help you a lot while learning c# coming from a java camp.(The link points to the differences about enums (scroll up / down for other things)

在从 Java 阵营学习 c# 时,此页面应该对您有很大帮助。(链接指向关于枚举的差异(向上/向下滚动其他内容)

回答by Chris S

Something like this I think:

我认为这样的事情:

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

Or combine the constants into the Planetclass as above

或者将常量组合到Planet上面的类中

回答by serg10

Java enums allow easy typesafe conversions from the name using the compiler-generated valueOf method, i.e.

Java 枚举允许使用编译器生成的 valueOf 方法从名称进行简单的类型安全转换,即

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

The equivalent for a raw enum in C# is more verbose:

C# 中原始枚举的等价物更详细:

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

However, if you go down the route sugegsted by Kent, you can easily implement a ValueOfmethod in your enum class.

但是,如果您沿着 Kent 建议的路线走下去,您可以轻松地ValueOf在您的枚举类中实现一个方法。

回答by Mikhail

In C# attributes can be used with enums. Good example of this programming pattern with detailed description is here(Codeproject)

在 C# 中,属性可以与枚举一起使用。这种具有详细描述的编程模式的好例子是here(Codeproject)

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

Edit:this question has been recently asked again and answered by Jon Skeet: What's the equivalent of Java's enum in C#?Private inner classes in C# - why aren't they used more often?

编辑:这个问题最近再次被问到并由 Jon Skeet 回答:C# 中 Java 的枚举相当于什么?C# 中的私有内部类 - 为什么它们不经常使用?

Edit 2:see the accepted answerwhich extends this approach in a very brilliant way!

编辑 2:查看已接受的答案,该答案以非常出色的方式扩展了这种方法!

回答by dmihailescu

The enum in Java is much more complex than C# enum and hence more powerful. Since it is just another compile time syntactical sugar I'm wondering if it was really worth having included the language given its limited usage in real life applications. Sometimes it's harder keeping stuff out of the language than giving up to the pressure to include a minor feature.

Java 中的 enum 比 C# 中的 enum 复杂得多,因此也更强大。由于它只是另一种编译时语法糖,我想知道考虑到它在现实生活应用程序中的使用有限,是否真的值得包含该语言。有时,将一些东西排除在语言之外比放弃包含一个次要特性的压力更难。

回答by finnw

In C# you can define extension methodson enums, and this makes up for some of the missing functionality.

在 C# 中,您可以在枚举上定义扩展方法,这弥补了一些缺失的功能。

You can define Planetas an enum and also have extension methods equivalent to surfaceGravity()and surfaceWeight().

您可以定义Planet为枚举,也可以使用与surfaceGravity()和等效的扩展方法surfaceWeight()

I have used custom attributes as suggested by Mikhail, but the same could be achieved using a Dictionary.

我已经按照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
}

回答by Paul Bruner

I suspect enums in C# are just constants internal to the CLR, but not that familiar with them. I have decompiled some classes in Java and I can tell you want Enums are once you convert.

我怀疑 C# 中的枚举只是 CLR 内部的常量,但对它们并不熟悉。我已经用 Java 反编译了一些类,我可以告诉你,一旦你转换,你就想要枚举。

Java does something sneaky. It treats the enum class as a a normal class with, as close as I can figure, using lots of macros when referencing the enum values. If you have a case statement in a Java class that uses enums, it replaces the enum references to integers. If you need to go to string, it creates an array of strings indexed by an ordinal that it uses in each class. I suspect to save on boxing.

Java 做了一些偷偷摸摸的事情。它将枚举类视为普通类,在引用枚举值时使用大量宏,尽我所能。如果在使用枚举的 Java 类中有 case 语句,它将替换枚举对整数的引用。如果您需要转到字符串,它会创建一个由它在每个类中使用的序数索引的字符串数组。我怀疑节省拳击。

If you download this decompiler you will get to see how it creates its class an integrates it. Rather fascinating to be honest. I used to not use the enum class because I thought it was to bloated for just an array of constants. I like it better than the limited way you can use them in C#.

如果你下载这个反编译器,你会看到它是如何创建它的类并集成它的。老实说比较迷人。我以前不使用 enum 类,因为我认为它只是为一组常量而膨胀。我比在 C# 中使用它们的有限方式更喜欢它。

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

http://members.fortunecity.com/neshkov/dj.html-- Java 反编译器

回答by Andrew Cooper

Here's another interesting idea which caters for the custom behaviour available in Java. I came up with the following Enumerationbase class:

这是另一个有趣的想法,它迎合了 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; }
    }
}

It's got a type parameter basically just so the ordinal count will work properly across different derived enumerations. Jon Skeet's Operatorexample from his answer to another question (http://stackoverflow.com/questions/1376312/whats-the-equivalent-of-javas-enum-in-c) above then becomes:

它基本上有一个类型参数,因此序数计数将在不同的派生枚举中正常工作。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);
    }
}

This gives a few advantages.

这提供了一些优点。

  • Ordinal support
  • Conversion to stringand intwhich makes switch statements feasible
  • GetType() will give the same result for each of the values of a derived Enumeration type.
  • The Static methods from System.Enumcan be added to the base Enumeration class to allow the same functionality.
  • 顺序支持
  • 转换为stringandint使 switch 语句可行
  • GetType() 将为派生的枚举类型的每个值提供相同的结果。
  • System.Enum可以将来自的静态方法添加到基本 Enumeration 类以允许相同的功能。