[java] C # 및 Java Enum (C #을 처음 사용하는 사용자)

나는 한동안 Java로 프로그래밍 해 왔으며 C #으로 작성된 프로젝트에 방금 던져졌습니다. C #에서 속도를 높이려고 노력 중이며 새 프로젝트의 여러 곳에서 열거 형이 사용되는 것을 보았지만 언뜻보기에 C #의 열거 형은 Java 1.5 + 구현보다 단순한 것으로 보입니다. 누구나 C #과 Java 열거 형의 차이점과 차이점을 극복하는 방법을 열거 할 수 있습니까? (언어 화염 전쟁을 시작하고 싶지 않고 Java에서했던 C #에서 몇 가지 작업을 수행하는 방법을 알고 싶습니다.) 예를 들어 누군가 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의 열거는 단순히 상수로 명명됩니다. 기본 유형은 정수 여야합니다. Java에서 열거 형은 형식의 명명 된 인스턴스와 비슷합니다. 이 유형은 상당히 복잡 할 수 있으며 예제에서 볼 수 있듯이 다양한 유형의 여러 필드를 포함합니다.

예제를 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;
    }
}


답변

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
}


답변

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 # 열거 형은 정수입니다. IMO 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에서 사용 가능한 사용자 정의 동작을 제공하는 또 다른 흥미로운 아이디어가 있습니다. 나는 다음 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; }
    }
}

기본적으로 형식 매개 변수가 있으므로 순서 수가 다른 파생 열거에서 올바르게 작동합니다. 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);
    }
}

이것은 몇 가지 장점을 제공합니다.

  • 서수 지원
  • 변환에 stringint있는 스위치 문이 가능합니다
  • GetType ()은 파생 열거 유형의 각 값에 대해 동일한 결과를 제공합니다.
  • 정적 메소드를 System.Enum동일한 기능을 허용하도록 기본 열거 클래스에 추가 할 수 있습니다.

답변

우리는 방금 C #에 대한 열거 확장을 만들었습니다
https://github.com/simonmau/enum_ext

typesafeenum에 대한 구현 일 뿐이지 만 훌륭하게 작동하므로 공유 할 패키지를 만들었습니다.

public sealed class Weekday : TypeSafeNameEnum<Weekday, int>
{
    public static readonly Weekday Monday = new Weekday(1, "--Monday--");
    public static readonly Weekday Tuesday = new Weekday(2, "--Tuesday--");
    public static readonly Weekday Wednesday = new Weekday(3, "--Wednesday--");
    ....

    private Weekday(int id, string name) : base(id, name)
    {
    }
}