views:

4134

answers:

6

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 ...]
+37  A: 

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.

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

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

HTH, Kent

Kent Boogaart
Doing that means you cannot use a for-each loop on the enums.
Richie_W
You changed the rules of the game ;)
Richie_W
Richie - this is software engineering, not a game. Unless you have a different agenda, solving problems in the idiomatic way with a language is better than trying to crowbar in an alien approach.
Barry Kelly
People like you give us a bad name, Barry. Get a sense of humor.
Richie_W
It's this sort of type-safe enum that us poor people forced to use Java 1.4 and below have to implement...Java 5's enums are perhaps the best feature of Java 5+, especially since they can be used in switch statements.
MetroidFan2002
Ok, of the answer's I've gotten so far, I like this one the best. This is along the lines I was thinking: if you want enum methods in C#, either change them to constants (as you did), or embed the enum inside a utility class (which might be harder to do with the "Planet" example).
Ogre Psalm33
One very minor point is Microsoft suggest enum names being pluralized afaik
Chris S
@Chris: only flag enums should be pluralized. That is, enumerations whose members are combined using the | operator.
Kent Boogaart
I think the example is a silly one anyway. What if you wanted to add a new planet? You would have to edit the code. This is why you would use normal classes for the planets and not enums in Java or in C#.
Mladen Mihajlovic
@Mladen: It depends entirely on the context. An enumeration of planets may be perfectly suited to a game that provides access to a limited number of planets. Changes to the code may be precisely what you want if a new planet is added to the game.
Kent Boogaart
@Kent true, but it still doesn't sound like good design to me. If I wanted to create an expansion for a game (for example) I would want all this info to be decoupled from the original code. Still, not saying that there isn't a place for enums, just that the example does not fit them in my eyes.
Mladen Mihajlovic
+1  A: 

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.

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.

JeeBee
+7  A: 

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.

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)

Richie_W
While your link gives an interesting, extensive overview of the similarities and differences between C# and Java, there are a lot of mistakes in the text (eg wrongly states that Java protected equals C# internal while it should be internal protected). So don't take everything there for granted :)
mafutrct
...I'm curious as to what other errors you have found?
Richie_W
+4  A: 

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;set;}
    public double Radius {get;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 the Planet class as above

Chris S
Not quite - the Planet constructor should be private; part of the point of enums is that they're a fixed set of values. The values would then be defined in the Planet class too.
Jon Skeet
A: 

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

// 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# 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 ValueOf method in your enum class.

serg10
The Java example is using a synthetic method generated by the compiler - nothing to do with generics at all. Enum does have a generic valueOf method, but that uses Class' generics and not Enum's.
Tom Hawtin - tackline
+4  A: 

In C# attributes can be used with enums. Good example of this programming pattern with detailed description is 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: http://stackoverflow.com/questions/1376312/whats-the-equivalent-of-javas-enum-in-c http://stackoverflow.com/questions/454218/private-inner-classes-in-c-why-arent-they-used-more-often

Mikhail
Nice! This feels only slightly clunky, but is otherwise a very acceptable method for adding extra data to an enum. I'm frankly amazed that it took someone this long to mention this great solution!
Ogre Psalm33