Wednesday 28 December 2011

Emulate Java Enums in C#

Enums are a really useful and simple programming construct. In C, C++ and C# an enum type is little more than a set of restricted numeric values that we can assign to a variable declared as such. In C#, there are 3 main static methods that we'll need when working with enums:
Let's say we have a Planet Enum:
enum Planet{ Mercury, Venus, Earth }

  • Get the string value from an enum:
    Enum.GetName
    Enum.GetName( typeof(Planet), mode)
  • Convert a string to an enum type
    Enum.Parse
    Planet myPlanet = (Planet)Enum.Parse(typeof(Planet), "Venus");
  • Retrieve an array of the values of the constants in a specified enumeration. Enum.GetValues
    Planet[] planets = Enum.GetValues(typeof(Planet));

So far so good, but sometimes it's not enough. Something rather common is the need to assign some extra string values to the enum (something beyond the name itself), for example a description. The common solution in C# for this is using a custom Attribute.

That should seem enough, and it used to be, until Java got enums in version 1.4, and we learnt from there that enums could provide a much richer (and more OOP) experience. The Planets sample is really interesting and mind opening. As you can see, an enum in Java is just a normal class, with data fields, properties and methods, the only special thing is that it has a limited number of possible values. Besides the infrastructure provided by the base Enum class, the Java compiler does a nice job for us, generating a class for our enum, inheriting it from Enum and adding some methods to it like valueOf and values (diassamble the .class for your test enum and check the bytecodes, do something like: javap -v MyEnum.class > MyEnum.bytecode)

One work around in C# for simple cases would be adding behavior to a normal Enum through Extension Methods (we can also add data by placing it in the helper class containing the Extension Methods, this way avoiding the Attributes approach that I pointed out before):

public enum Planet
{ 
 Mercury, 
 Venus, 
 Earth 
}

public class PlanetData
{
 public double Mass
 {get;set;}
 public double Radius
 {get;set;}
 
}

public static class PlanetHelper
{
 private static Dictionary planets = new Dictionary()
 {
  {Planet.Mercury, new PlanetData(){Mass = 3.30, Radius = 2.43}},
  {Planet.Venus, new PlanetData(){Mass = 4.86, Radius = 6.05}},
  {Planet.Earth, new PlanetData(){Mass = 5.97, Radius = 6.37}}
 };
 
 public static readonly double G = 6.67;
 
 public static double GetRadius(this Planet pl)
 {
  return planets[pl].Radius;
 }
 
 public static double GetMass(this Planet pl)
 {
  return planets[pl].Mass;
 }
 
 public static double GetSurfaceGravity(this Planet pl) 
 {
  return G * pl.GetMass() / pl.GetRadius() * pl.GetRadius();
    }
 
    public static double GetSurfaceWeight(this Planet pl, double otherMass) 
 {
        return otherMass * pl.GetSurfaceGravity();
    }
}

You can check the source here

Something like the above should be enough for most cases, but anyway, what if we want to simulate as much as possible the Java case?. Reading this StackOverflow discussion is a must

. The solution given there is pretty good, but why not to try to go the Java way and generalize as much functionality as possible to a Base class.

Before showing my solution here, I'd like to note that Java enums have another very interesting feature that I found out when reading this excellent article. One method can vary across instances, I mean, each enum value can have a different implementation for one method. Well, hopefully, in the .Net arena delegates come to our rescue, making it very simple to add that extra feature

All in all, this is what I came up with:

public abstract class AdvancedEnum
{
 public Enum InternalEnum;
 //notice that this Name (as Java's Name) is similar .Net's Enum.GetName()
 public string Name
 {
  get
  {
   return this.InternalEnum.ToString();
  }
 }
 
 //notice that this ValueOf (as Java's ValueOf) is the equivalent to .Net's Enum.Parse()
 public static T ValueOf(string name)
 {
  return (T)(typeof(T).GetField(name, BindingFlags.Public | BindingFlags.Static).GetValue(null));
 }
 
 public static IEnumerable Values()
 {
        //this is really weird, for some odd reason you need to use "+" instead of "."
  string tpStr = typeof(T).Name + "+" + typeof(T).Name + "Enum";
  Type tp = Type.GetType(tpStr);
  //notice that .Net warranties that values are returned in the same order as they were declared
  foreach(var item in Enum.GetValues(tp))
  {
   yield return ValueOf(Enum.GetName( tp, item));
  }
 }
}

public class Planet: AdvancedEnum
{
 //naming is important here, we use as a convention that the internal enum must be named thus: className + "Enum"
 public enum PlanetEnum
 {
  MERCURY,
  VENUS
 }
 
 //"method" that will vary across instances
 public Action Live;
 
 public static readonly double G = 6.67;
 
 private double mass;   // in kilograms
 public double Mass
 {
  get
  {
   return this.mass;
  }
 }
 private double radius;
 public double Radius
 {
  get
  {
   return this.radius;
  }
 }

 private Planet(PlanetEnum internalEnum, double mass, double radius, Action liveAction)
 {
  this.InternalEnum = internalEnum;
  this.mass = mass;
  this.radius = radius;
  this.Live = liveAction;
 }
 
 public static readonly Planet MERCURY = new Planet(PlanetEnum.MERCURY, 3, 2, () => Console.WriteLine("living in Mercury is impossible for humans"));
    public static readonly Planet VENUS = new Planet (PlanetEnum.VENUS, 4, 6, () => Console.WriteLine("living in Venu would have been possible for humans milleniums ago"));
 
 public double GetSurfaceGravity() 
 {
  return G * this.Mass / this.Radius * this.Radius;
    }
 
}

You can check the source here

I plan to experiment a bit with enums in JavaScript, but for the moment, you can check this article I came across with

No comments:

Post a Comment