27

What's the equivalent of Java's enum in C#?

Nosrama
  • 14,530
  • 13
  • 49
  • 58

6 Answers6

39

Full Java enum functionality isn't available in C#. You can come reasonably close using nested types and a private constructor though. For example:

using System;
using System.Collections.Generic;
using System.Xml.Linq;

public abstract class Operator
{
    public static readonly Operator Plus = new PlusOperator();
    public static readonly Operator Minus = 
         new GenericOperator((x, y) => x - y);
    public static readonly Operator Times = 
         new GenericOperator((x, y) => x * y);
    public static readonly Operator Divide = 
         new GenericOperator((x, y) => x / y);

    // Prevent other top-level types from instantiating
    private Operator()
    {
    }

    public abstract int Execute(int left, int right);

    private class PlusOperator : Operator
    {
        public override int Execute(int left, int right)
        {
            return left + right;
        }
    }

    private class GenericOperator : Operator
    {
        private readonly Func<int, int, int> op;

        internal GenericOperator(Func<int, int, int> op)
        {
            this.op = op;
        }

        public override int Execute(int left, int right)
        {
            return op(left, right);
        }
    }
}

Of course you don't have to use nested types, but they give the handy "custom behaviour" part which Java enums are nice for. In other cases you can just pass arguments to a private constructor to get a well-known restricted set of values.

A few things this doesn't give you:

  • Ordinal support
  • Switch support
  • EnumSet
  • Serialization/deserialization (as a singleton)

Some of that could probably be done with enough effort, though switch wouldn't really be feasible without hackery. Now if the language did something like this, it could do interesting things to make switch work by making the hackery automatic (e.g. declaring a load of const fields automatically, and changing any switch over the enum type to a switch over integers, only allowing "known" cases .)

Oh, and partial types mean you don't have to have all of the enum values in the same file. If each value got quite involved (which is definitely possible) each could have its own file.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I think it would be trivial to support switch statements (and ordinals) by using an implicit conversion to int, right? What did I overlook? – lmat - Reinstate Monica Nov 07 '11 at 20:37
  • 2
    @LimitedAtonement: How would you represent the *constant* values that you'd need for the case expressions? – Jon Skeet Nov 07 '11 at 22:06
  • I read your article recently about there being few times when non-private fields are justified. Is this one of those times? – Andrew Cooper May 28 '12 at 17:56
  • @AndrewCooper: Yes - although some people may prefer to expose these via properties anyway. – Jon Skeet May 28 '12 at 18:09
  • I like this solution and it's inspired some work I've done in a port from Java to C# recently. I came up with what I think is an interesting solution supported by a base `Enumeration` class, which adds ordinal and name support. See my answer for details. I'm planning to blog this soon, but I'd appreciate your feedback on my solution first. – Andrew Cooper Jun 22 '12 at 13:51
  • I'm currently getting a build error copy/pasting this code in VS2012. I'm looking at this pattern for a series of Evaluators across multiple object types. "Incompatible anonymous function signature." my understanding of anonymous function signatures has always been tenuous, but it looks correct to me. – Jonathan Nov 01 '13 at 18:47
  • EDIT to above: Found it. `private class GenericOperator : Operator { private readonly Func op; internal GenericOperator(Func op) { this.op = op; } public override int Execute(int left, int right) { return op(left, right); } }` – Jonathan Nov 01 '13 at 19:39
21

Enums are one of the few language features that is better implemented in java than c#. In java, enums are full fledged named instances of a type, while c# enums are basically named constants.

That being said, for the basic case, they will look similar. However in java, you have more power, in that you can add behavior to the individual enums, as they are full fledged classes.

is there some feature in particular you are looking for?

Chi
  • 22,624
  • 6
  • 36
  • 37
  • 8
    "They will work the same" - not IMO, because constant numbers can't have custom *behaviour*, unlike Java enum values. – Jon Skeet Sep 03 '09 at 23:21
  • 2
    yup, hence the qualification 'for basic use'. I agree that one of the nice things about java enums is the custom behavior – Chi Sep 03 '09 at 23:30
  • 2
    I like Java Enums than C# Enums. With C# Enums, you spread a lot of Switch Statements Smell, and a lot of these code scattered in switch statements could have been placed as methods associated with each Enum. –  Mar 14 '12 at 03:12
6

Here's another interesting idea. I came up with the following Enumeration base class:

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's Operator example above then becomes:

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 string and int which 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.Enum can be added to the base Enumeration class to allow the same functionality.
Andrew Cooper
  • 32,176
  • 5
  • 81
  • 116
  • I'm doing some Java to C# conversion so I looked at your answer a month ago and thought 'a bit too much trouble for my needs'... Now I'm back again, seeing a lot more value, as I need the 'retrieve enum by (string) name' functionality. Kudos! – Cristian Diaconescu Jan 04 '13 at 16:36
  • @Andrew Cooper , re: "This gives a few advantages. * Ordinal support * Conversion to string and int which makes switch statements feasible" - seems this still does not give switch statement support, as compiler complains that case statements need a constant value. It accepts neither (int) cast nor .Ordinal value as const. – tomosius Mar 11 '16 at 01:16
5

You could probably use the old typesafe enum pattern that we used in Java before we got real ones (assuming that the ones in C# really aren't classes as a comment claims). The pattern is described just before the middle of this page

Bill K
  • 62,186
  • 18
  • 105
  • 157
2
//Review the sample enum below for a template on how to implement a JavaEnum.
//There is also an EnumSet implementation below.

public abstract class JavaEnum : IComparable {
    public static IEnumerable<JavaEnum> Values {
        get {
            throw new NotImplementedException("Enumeration missing");
        }
    }

    public readonly string Name;

    public JavaEnum(string name) {
        this.Name = name;
    }

    public override string ToString() {
        return base.ToString() + "." + Name.ToUpper();
    }

    public int CompareTo(object obj) {
        if(obj is JavaEnum) {
            return string.Compare(this.Name, ((JavaEnum)obj).Name);
        } else {
            throw new ArgumentException();
        }
    }


    //Dictionary values are of type SortedSet<T>
    private static Dictionary<Type, object> enumDictionary;
    public static SortedSet<T> RetrieveEnumValues<T>() where T : JavaEnum {
        if(enumDictionary == null) {
            enumDictionary = new Dictionary<Type, object>();
        }
        object enums;
        if(!enumDictionary.TryGetValue(typeof(T), out enums)) {
            enums = new SortedSet<T>();
            FieldInfo[] myFieldInfo = typeof(T).GetFields(BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.Public);
            foreach(FieldInfo f in myFieldInfo) {
                if(f.FieldType == typeof(T)) {
                    ((SortedSet<T>)enums).Add((T)f.GetValue(null));
                }
            }
            enumDictionary.Add(typeof(T), enums);
        }
        return (SortedSet<T>)enums;
    }
}


//Sample JavaEnum
public class SampleEnum : JavaEnum {
    //Enum values
    public static readonly SampleEnum A = new SampleEnum("A", 1);
    public static readonly SampleEnum B = new SampleEnum("B", 2);
    public static readonly SampleEnum C = new SampleEnum("C", 3);

    //Variables or Properties common to all enums of this type
    public int int1;
    public static int int2 = 4;
    public static readonly int int3 = 9;

    //The Values property must be replaced with a call to JavaEnum.generateEnumValues<MyEnumType>() to generate an IEnumerable set.
    public static new IEnumerable<SampleEnum> Values {
        get {
            foreach(var e in JavaEnum.RetrieveEnumValues<SampleEnum>()) {
                yield return e;
            }
            //If this enum should compose several enums, add them here
            //foreach(var e in ChildSampleEnum.Values) {
            //    yield return e;
            //}
        }
    }

    public SampleEnum(string name, int int1)
        : base(name) {
        this.int1 = int1;
    }
}


public class EnumSet<T> : SortedSet<T> where T : JavaEnum {
    // Creates an enum set containing all of the elements in the specified element type.
    public static EnumSet<T> AllOf(IEnumerable<T> values) {
        EnumSet<T> returnSet = new EnumSet<T>();
        foreach(T item in values) {
            returnSet.Add(item);
        }
        return returnSet;
    }

    // Creates an enum set with the same element type as the specified enum set, initially containing all the elements of this type that are not contained in the specified set.
    public static EnumSet<T> ComplementOf(IEnumerable<T> values, EnumSet<T> set) {
        EnumSet<T> returnSet = new EnumSet<T>();
        foreach(T item in values) {
            if(!set.Contains(item)) {
                returnSet.Add(item);
            }
        }
        return returnSet;
    }

    // Creates an enum set initially containing all of the elements in the range defined by the two specified endpoints.
    public static EnumSet<T> Range(IEnumerable<T> values, T from, T to) {
        EnumSet<T> returnSet = new EnumSet<T>();
        if(from == to) {
            returnSet.Add(from);
            return returnSet;
        }
        bool isFrom = false;
        foreach(T item in values) {
            if(isFrom) {
                returnSet.Add(item);
                if(item == to) {
                    return returnSet;
                }
            } else if(item == from) {
                isFrom = true;
                returnSet.Add(item);
            }
        }
        throw new ArgumentException();
    }

    // Creates an enum set initially containing the specified element(s).
    public static EnumSet<T> Of(params T[] setItems) {
        EnumSet<T> returnSet = new EnumSet<T>();
        foreach(T item in setItems) {
            returnSet.Add(item);
        }
        return returnSet;
    }

    // Creates an empty enum set with the specified element type.
    public static EnumSet<T> NoneOf() {
        return new EnumSet<T>();
    }

    // Returns a copy of the set passed in.
    public static EnumSet<T> CopyOf(EnumSet<T> set) {
        EnumSet<T> returnSet = new EnumSet<T>();
        returnSet.Add(set);
        return returnSet;
    }

    // Adds a set to an existing set.
    public void Add(EnumSet<T> enumSet) {
        foreach(T item in enumSet) {
            this.Add(item);
        }
    }

    // Removes a set from an existing set.
    public void Remove(EnumSet<T> enumSet) {
        foreach(T item in enumSet) {
            this.Remove(item);
        }
    }
}
Jim
  • 869
  • 1
  • 10
  • 16
0

enum , or do you need something in particular that Java enums have but c# doesn't ?

leeeroy
  • 11,216
  • 17
  • 52
  • 54
  • In Java, enums can have multiple associated values for each enum, methods with logic, and constructors. At least as of C# 3.0, C# enums are still just a list of constants, with a possible associated value. – Ogre Psalm33 Apr 22 '10 at 20:06
  • 7
    -1: Java enums are *so* much more powerful than C# ones. – Kramii Jan 05 '11 at 11:00
  • +1 to counter Kramii's -1. C# enums can have extension methods which makes up for the absence of some of those features. – finnw Jan 22 '11 at 14:05