Master Chief
Master Chief

Reputation: 2541

Cannot understand the behavior of Enums

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.

Upvotes: 5

Views: 257

Answers (2)

bstenzel
bstenzel

Reputation: 1241

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.

Upvotes: 7

Ahsan Ahmad
Ahsan Ahmad

Reputation: 999

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

Upvotes: -1

Related Questions