5

I was practicing enums in C#, and I am not able to understand the output of these

void Main()
{
    MyEnum a = MyEnum.Top;
    Console.WriteLine(a);
}

For this test my enum and output are

enum MyEnum
{
    Left, Right, Top, Bottom // Top
}

enum MyEnum
{
    Left, Right, Top = 0, Bottom // Left
}

I thought at rum time program chooses the 1st item with value 0, to confirm this I assigned value of 0 to Bottom

enum MyEnum
{
    Left, Right, Top = 0, Bottom = 0 // Bottom
}

Then I thought maybe program chooses the 1st item with value 0 but searches alphabetically so I changed Top to ATop and changed the test case

void Main()
{
    MyEnum a = MyEnum.ATop;
    Console.WriteLine(a);
}

enum MyEnum
{
    Left, Right, ATop = 0, Bottom = 0 // Bottom
}

enum MyEnum
{
    Left, Right, ATop = 0, Bottom // Left
}

I know nobody uses enum in this way, but I want to know this particular behavior of Enum.

Master Chief
  • 2,520
  • 19
  • 28
  • But then why the output changes when I assign same value to 3 enums(Left is assigned 0 automatically I assume, and 2 explicit assignment by me) – Master Chief Sep 08 '14 at 05:36
  • Well, by having more than 1 enum values with the same enum value, you make the values indistingable for each other. If you try to print the name of the value, you enter undefined behavior territory, which means the implementation is free to provide you with any of the relevant names. Why it choses one over another is an implemenation detail and could potentially change anytime. – Falanwe Sep 08 '14 at 06:28
  • 1
    Which enum name is returned is undocumented behavior, [see my answer here](http://stackoverflow.com/questions/24852609/how-are-ambiguous-enum-values-resolved-in-c/24852646#24852646). – Lasse V. Karlsen Sep 08 '14 at 06:31
  • This indeed a rum time issue ;p @LasseV.Karlsen – leppie Sep 08 '14 at 06:45

2 Answers2

7

Seems like you figured out already that the compiler starts counting at zero by default.

enum MyEnum
{
    Left, Right, Top = 0, Bottom = 0 // Bottom
}

gets translated to this

.class nested private auto ansi sealed MyEnum
    extends [mscorlib]System.Enum
{
    .field public specialname rtspecialname int32 value__
    .field public static literal valuetype X/MyEnum Left = int32(0)
    .field public static literal valuetype X/MyEnum Right = int32(1)
    .field public static literal valuetype X/MyEnum Top = int32(0)
    .field public static literal valuetype X/MyEnum Bottom = int32(0)
}

The runtime actually works with the underlying types most of the time. So the funny thing is that this here

static void Main()
{
    MyEnum a = MyEnum.Top;
    Console.WriteLine(a);

    Console.ReadKey();
}

doesn't even use the actual enum member:

.method private hidebysig static 
    void Main () cil managed 
{
    .maxstack 1
    .entrypoint
    .locals init (
        [0] valuetype X/MyEnum a
    )

    IL_0000: nop
    IL_0001: ldc.i4.0
    IL_0002: stloc.0
    IL_0003: ldloc.0
    IL_0004: box X/MyEnum
    IL_0009: call void [mscorlib]System.Console::WriteLine(object)
    IL_000e: nop
    IL_000f: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
    IL_0014: pop
    IL_0015: ret
}

It just uses the value 0. The decision making of which name to print out with Console.WriteLine begins in System.Enum.ToString and climaxes in System.Type.GetEnumName(object).

public virtual string GetEnumName(object value)
{
    if (value == null)
    {
        throw new ArgumentNullException("value");
    }
    if (!this.IsEnum)
    {
        throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnum"), "enumType");
    }
    Type type = value.GetType();
    if (!type.IsEnum && !Type.IsIntegerType(type))
    {
        throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnumBaseTypeOrEnum"), "value");
    }
    Array enumRawConstantValues = this.GetEnumRawConstantValues();
    int num = Type.BinarySearch(enumRawConstantValues, value);
    if (num >= 0)
    {
        string[] enumNames = this.GetEnumNames();
        return enumNames[num];
    }
    return null;
}

As you can see the actual way to search for the name to print is a binary search through the field names (found by reflection).

This is just the current implementation, though and might differ with different compilers and/or runtime versions. The language specification doesn't guarantee any particular order or outcome for code like the one above.

bstenzel
  • 1,231
  • 9
  • 14
  • 3
    The fact that it uses binary search to find which name to show is undocumented behavior so the OP should not rely on the code always returning the same name for a duplicated value. Additionally, just adding 1 value to the enum might change the outcome. – Lasse V. Karlsen Sep 08 '14 at 06:34
  • Yes, it's just the current implementation of the compiler and runtime version I used. I've added a paragraph in regard of that. – bstenzel Sep 08 '14 at 06:40
-1

Enum is nothing but naming some constant.

Like we have some fruits. Apple, Banana, Orange. we have a scenario that, if a baby is aged 1 year it will get Orange, if 2 will get Banana, if 3 it will get Apple. so how we write this?

one solution is like :

    int fruiteType = 0;
    if(ageOfBaby == 1)
        fruiteType == 1;//assuming Orange = 1; 
    if(ageOfBaby == 2)
        fruiteType == 2;//assuming Banana = 2; 
    if(ageOfBaby == 3)
        fruiteType == 3;//assuming Apple = 3; 

we can do it using Enum smartly. like :

    enum Fruits {Orange, Banana, Apple};

    int fruiteType = 0;
    if(ageOfBaby == 1)
        fruiteType == (int)Fruits.Orange;
    if(ageOfBaby == 2)
        fruiteType == (int)Fruits.Banana;
    if(ageOfBaby == 3)
        fruiteType == (int)Fruits.Apple;

by Default you will get 0, 1, 2 in fruiteType variable in above conditions respectively. if you want to change the the default you can define like:

  enum Fruits {Orange = 1, Banana, Apple};

why this is important. when we assume such values we may forgot the sequence so that it creates trouble. but when we named them we never forget which value is standing for fruitType = 1.

Thanks

Ahsan Ahmad
  • 999
  • 1
  • 9
  • 21