Saturday, November 5, 2016

C# vs Java : Enums

The syntax for declaring an enum looks the same in both C# and Java
enum DaysOfWeek {
   Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
}
Likewise outputting an enum's element results in it's name being outputed
// Monday
Console.WriteLine(DaysOfWeek.Monday); // C#
// Monday
System.out.println(DaysOfWeek.Monday); // Java
An important distinction between the two languages is that enums in Java are a reference type while in C# they are a value type, albeit their own special kind of value type that's separate from ints and doubles. See Microsoft's Value Types (C# Reference)

Let's explore this further. When I hear the phrase "reference type" I automatically think objects. So does that mean I can use the new operator in Java to instantiate an instance of an enum?
// Compiler error
DaysOfWeek test = new DaysOfWeek();
Recall that enums are "a set of predefined constants". (The Java Tutorials : Enum Types) So it would make sense that you can't instantiate a new element that isn't already in the set. I can, however, set an enum variable to null.
DaysOfWeek test = null;
Recall from my post on Java Enums that they can have constructors, methods, and fields. Java enums can even implement interfaces.
public interface SomeInterface {
}

enum DaysOfWeek implements SomeInterface {
   Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday;   
}
They cannot, however, extend from another class or enum
// compiler error
enum DaysOfWeek extends SomeClass {
   Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday;   
}
What's really important to remember with reference types is that when you pass them to a method you're passing the value of the reference to the object. So any modifications you make to the object remain even after the method has returned.
public enum SomeEnum {
   element;
   
   private String str = "";
   
   public void setStr(String str) {
      this.str = str;
   }
   
   public String getStr() {
      return str;
   }   
}

public class Klass  {
   
   public static void modify(SomeEnum fr) {
      fr.setStr("Salut le Monde");
   }
   
   public static void main(String... args) {
      SomeEnum test = SomeEnum.element;
      
      test.setStr("Hello World");
      System.out.println(test.getStr()); // Hello World
            
      modify(test);
      System.out.println(test.getStr()); // Salut le Monde
   }
}
Ok now let's explore C# enums. Recall that they are a value type. So does that mean I can assign it any number I want? Strangely the answer is yes but a cast may be needed.  I should also point out that the following will not work in Java.
// compiler error
// DaysOfWeek wrong = 3; 

DaysOfWeek tooLow = (DaysOfWeek)(-999);
Console.WriteLine(tooLow);
   
DaysOfWeek tooHigh = (DaysOfWeek)999;    
Console.WriteLine(tooHigh);
Zero appears to be the lone exception to this.
DaysOfWeek zero = 0;
You can do addition and subtraction on C# enums, but not multiplication or division.
// Monday
Console.WriteLine(String.Format("x = {0}", (DaysOfWeek.Sunday + 1)));

// Sunday
Console.WriteLine(String.Format("x = {0}", (DaysOfWeek.Monday - 1)));

// compiler error
// Console.WriteLine(String.Format("x = {0}", (DaysOfWeek.Sunday * 2)));

// compiler error
// Console.WriteLine(String.Format("x = {0}", (DaysOfWeek.Wednesday / 2)));
There are many techniques for representing an enum variable that hasn't been assigned a value. But first, you need to know that you can't assign null to an enum variable in C#.
// compiler error
DaysOfWeek x = null;
Well that's not entirely true. You can assign it a null value, but you have to use the Nullable type to do it. There are two of ways doing this.
Nullable<DaysOfWeek> longWay = null;   

DaysOfWeek? shortWay = null;
Another technique is to give the enum an extra element specifically to represent null.
enum DaysOfWeek extends SomeClass {
   Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, NULL;
}

DaysOfWeek x = DaysOfWeek.NULL;
And still another way is to assign it a non-positive number. This is, of course, assuming all the elements in the enum have a positive value:
DaysOfWeek x = (DaysOfWeek)(-1);
Did I mention you can change the value of enum elements? You can even make all the elements have the same value and the compiler won't complain. This is something that you can't do in Java.
enum DaysOfWeek {
 Sunday = 1, Monday = 1, Tuesday = 1, Wednesday = 1, Thursday = 1, Friday = 1, Saturday  = 1
}
Given that an enum variable can have pretty much any value you can use the IsDefined method to check if a given enum value is valid.
DaysOfWeek x = (DaysOfWeek)(-12);

// False   
Console.WriteLine(Enum.IsDefined(typeof(DaysOfWeek), x));
Recall that you can't use the new operator to instantiate a Java enum. Well in C# you can, but its unclear what its actually instantiating. I've always used the word instantiate when referring to object creation.
DaysOfWeek day = new DaysOfWeek();
This will assign it the last item that has a value of zero.
enum DaysOfWeek {
  Sunday=5, Monday=3, Tuesday=1, Wednesday=0, Thursday=4, Friday=0, Saturday=0
}

// Saturday	
Console.WriteLine(new DaysOfWeek());
And if there are no items with a value of zero, it assigns it the value 0.
enum DaysOfWeek {
  Sunday=1, Monday=1, Tuesday=1, Wednesday=1, Thursday=1, Friday=1, Saturday=1
}

// 0
Console.WriteLine(new DaysOfWeek());

Here's how to use Enums with switch statement in Java.
DaysOfWeek day = DaysOfWeek.Monday;

switch(day) {
  case Monday:
    System.out.println("Monday");
    break;
  default:
    System.out.println("????");
    break;
}
Here's what that looks like in C#. Notice that I have to prefix the element name in the case statement with the name of the Enum.
DaysOfWeek day = DaysOfWeek.Monday;

switch(day) {
  case DaysOfWeek.Monday:
    Console.WriteLine("Monday");
    break;
  default:
    Console.WriteLine("????");
    break;
}

In Java you use the ordinal method to get the position where an enum element is declared at.
// 1
System.out.println(DaysOfWeek.Monday.ordinal());
In C# you cast it to an integer:
Console.WriteLine((int)DaysOfWeek.Monday);
Alternatively, you could do this:
System.Array enumValues = Enum.GetValues(typeof(DaysOfWeek));
Console.WriteLine(Array.IndexOf(enumValues, DaysOfWeek.Monday));

Here's how, in Java, to iterate over each element in an enum:
for (DaysOfWeek day : DaysOfWeek.values()) {
  System.out.print(day + " ");
}
Here's how to do the same thing in C#:
foreach(DaysOfWeek day in Enum.GetValues(typeof(DaysOfWeek)))  {
  Console.Write(day + " ");
}

Both .NET and Java enums are backed by a class. Don't ask me how this works in .NET, but here's proof.
// System.Enum
Console.WriteLine(String.Format("{0}", DaysOfWeek.Monday.GetType().BaseType));
System.Enum is an abstract class according to the .NET reference.

And here's the Java version..
// class my.eclipse.DaysOfWeek
System.out.println(DaysOfWeek.Monday.getClass());

References

StackOverflow, C# : How to set enum to null
(.NET) Enum Class
C#/.NET Little Wonders: Fun With Enum Methods
(.NET) Value Types

No comments:

Post a Comment