Tutorial :Why does Enum.GetValues() return names when using “var”?



Question:

Can anyone explain this?

alt text http://www.deviantsart.com/upload/g4knqc.png

using System;    namespace TestEnum2342394834  {      class Program      {          static void Main(string[] args)          {              //with "var"              foreach (var value in Enum.GetValues(typeof(ReportStatus)))              {                  Console.WriteLine(value);              }                //with "int"              foreach (int value in Enum.GetValues(typeof(ReportStatus)))              {                  Console.WriteLine(value);              }            }      }        public enum ReportStatus      {          Assigned = 1,          Analyzed = 2,          Written = 3,          Reviewed = 4,          Finished = 5      }  }  


Solution:1

Enum.GetValues is declared as returning Array.
The array that it returns contains the actual values as ReportStatus values.

Therefore, the var keyword becomes object, and the value variable holds (boxed) typed enum values.
The Console.WriteLine call resolves to the overload that takes an object and calls ToString() on the object, which, for enums, returns the name.

When you iterate over an int, the compiler implicitly casts the values to int, and the value variable holds normal (and non-boxed) int values.
Therefore, the Console.WriteLine call resolves to the overload that takes an int and prints it.

If you change int to DateTime (or any other type), it will still compile, but it will throw an InvalidCastException at runtime.


Solution:2

According to the MSDN documentation, the overload of Console.WriteLine that takes an object internally calls ToString on its argument.

When you do foreach (var value in ...), your value variable is typed as object (since, as SLaks points out, Enum.GetValues returns an untyped Array) and so your Console.WriteLine is calling object.ToString which is overriden by System.Enum.ToString. And this method returns the name of the enum.

When you do foreach (int value in ...), you're casting the enum values to int values (instead of object); so Console.WriteLine is calling System.Int32.ToString.


Solution:3

FWIW, here's the disassembled code from Enum.GetValues() (via Reflector):

[ComVisible(true)]  public static Array GetValues(Type enumType)  {      if (enumType == null)      {          throw new ArgumentNullException("enumType");      }      if (!(enumType is RuntimeType))      {          throw new ArgumentException(Environment.GetResourceString("Arg_MustBeType"), "enumType");      }      if (!enumType.IsEnum)      {          throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnum"), "enumType");      }      ulong[] values = GetHashEntry(enumType).values;      Array array = Array.CreateInstance(enumType, values.Length);      for (int i = 0; i < values.Length; i++)      {          object obj2 = ToObject(enumType, values[i]);          array.SetValue(obj2, i);      }      return array;  }  

Looks like what everyone's saying about the var being an object and calling object.ToString() returning the name is correct...


Solution:4

You implicitely call ToString() on each element when you use Console.WriteLine.

And when you say you want an int (using the explicit type) it will convert it to an int - and then ToString() it.

The first one is the Enum value ToString()'ed


Solution:5

EDIT: added some sample code that explores many (perhaps all?) possible ways of iterating over the array.

Enum types are considered to be "derived" from int by default. You can choose to derive it from one of the other integer types if you want, such as byte, short, long, etc.

In both cases, the call to Enum.GetValues is returning an array of ReportStatus objects.

Using the var keyword in the first loop tells the compiler to use the specified type of the array, which is ReportStatus, to determine the type of the value variable. The ToString implementation for enums is to return the name of the enum entry, not the integer value it represents, which is why the names are being output from the first loop.

Using an int variable in the second loop causes the values returned by Enum.GetValues to be implicitly converted from ReportStatus to int. Calling ToString on an int will, of course, return a string representing the integer value. The implicit conversion is what causes the difference in behavior.

UPDATE: As others have pointed out, the Enum.GetValues function returns an object typed as Array, and as a result it is an enumerable of Object types, not ReportStatus types.

Regardless, the end result is the same whether iterating over Array or ReportStatus[]:

class Program  {      enum ReportStatus      {          Assigned = 1,          Analyzed = 2,          Written = 3,          Reviewed = 4,          Finished = 5,      }        static void Main(string[] args)      {          WriteValues(Enum.GetValues(typeof(ReportStatus)));            ReportStatus[] values = new ReportStatus[] {              ReportStatus.Assigned,              ReportStatus.Analyzed,              ReportStatus.Written,              ReportStatus.Reviewed,              ReportStatus.Finished,          };            WriteValues(values);      }        static void WriteValues(Array values)      {          foreach (var value in values)          {              Console.WriteLine(value);          }            foreach (int value in values)          {              Console.WriteLine(value);          }      }        static void WriteValues(ReportStatus[] values)      {          foreach (var value in values)          {              Console.WriteLine(value);          }            foreach (int value in values)          {              Console.WriteLine(value);          }      }  }  

Just for some extra fun, I've added some code below demonstrating several different ways of iterating over the specified array with a foreach loop, including comments that describes in detail what's going on in each case.

class Program  {      enum ReportStatus      {          Assigned = 1,          Analyzed = 2,          Written = 3,          Reviewed = 4,          Finished = 5,      }        static void Main(string[] args)      {          Array values = Enum.GetValues(typeof(ReportStatus));            Console.WriteLine("Type of array: {0}", values.GetType().FullName);            // Case 1: iterating over values as System.Array, loop variable is of type System.Object          // The foreach loop uses an IEnumerator obtained from System.Array.          // The IEnumerator's Current property uses the System.Array.GetValue method to retrieve the current value, which uses the TypedReference.InternalToObject function.          // The value variable is passed to Console.WriteLine(System.Object).          // Summary: 0 box operations, 0 unbox operations, 1 usage of TypedReference          Console.WriteLine("foreach (object value in values)");          foreach (object value in values)          {              Console.WriteLine(value);          }            // Case 2: iterating over values as System.Array, loop variable is of type ReportStatus          // The foreach loop uses an IEnumerator obtained from System.Array.          // The IEnumerator's Current property uses the System.Array.GetValue method to retrieve the current value, which uses the TypedReference.InternalToObject function.          // The current value is immediatly unboxed as ReportStatus to be assigned to the loop variable, value.          // The value variable is then boxed again so that it can be passed to Console.WriteLine(System.Object).          // Summary: 1 box operation, 1 unbox operation, 1 usage of TypedReference          Console.WriteLine("foreach (ReportStatus value in values)");          foreach (ReportStatus value in values)          {              Console.WriteLine(value);          }            // Case 3: iterating over values as System.Array, loop variable is of type System.Int32.          // The foreach loop uses an IEnumerator obtained from System.Array.          // The IEnumerator's Current property uses the System.Array.GetValue method to retrieve the current value, which uses the TypedReference.InternalToObject function.          // The current value is immediatly unboxed as System.Int32 to be assigned to the loop variable, value.          // The value variable is passed to Console.WriteLine(System.Int32).          // Summary: 0 box operations, 1 unbox operation, 1 usage of TypedReference          Console.WriteLine("foreach (int value in values)");          foreach (int value in values)          {              Console.WriteLine(value);          }            // Case 4: iterating over values as ReportStatus[], loop variable is of type System.Object.          // The foreach loop is compiled as a simple for loop; it does not use an enumerator.          // On each iteration, the current element of the array is assigned to the loop variable, value.          // At that time, the current ReportStatus value is boxed as System.Object.          // The value variable is passed to Console.WriteLine(System.Object).          // Summary: 1 box operation, 0 unbox operations          Console.WriteLine("foreach (object value in (ReportStatus[])values)");          foreach (object value in (ReportStatus[])values)          {              Console.WriteLine(value);          }            // Case 5: iterating over values as ReportStatus[], loop variable is of type ReportStatus.          // The foreach loop is compiled as a simple for loop; it does not use an enumerator.          // On each iteration, the current element of the array is assigned to the loop variable, value.          // The value variable is then boxed so that it can be passed to Console.WriteLine(System.Object).          // Summary: 1 box operation, 0 unbox operations          Console.WriteLine("foreach (ReportStatus value in (ReportStatus[])values)");          foreach (ReportStatus value in (ReportStatus[])values)          {              Console.WriteLine(value);          }            // Case 6: iterating over values as ReportStatus[], loop variable is of type System.Int32.          // The foreach loop is compiled as a simple for loop; it does not use an enumerator.          // On each iteration, the current element of the array is assigned to the loop variable, value.          // The value variable is passed to Console.WriteLine(System.Int32).          // Summary: 0 box operations, 0 unbox operations          Console.WriteLine("foreach (int value in (ReportStatus[])values)");          foreach (int value in (ReportStatus[])values)          {              Console.WriteLine(value);          }            // Case 7: The compiler evaluates var to System.Object.  This is equivalent to case #1.          Console.WriteLine("foreach (var value in values)");          foreach (var value in values)          {              Console.WriteLine(value);          }            // Case 8: The compiler evaluates var to ReportStatus.  This is equivalent to case #5.          Console.WriteLine("foreach (var value in (ReportStatus[])values)");          foreach (var value in (ReportStatus[])values)          {              Console.WriteLine(value);          }      }  }  

-- Updated my comments in the sample above; upon double-checking I saw that the System.Array.GetValue method actually uses the TypedReference class in order to extract an element of the array and return it as System.Object. I had originally written that there was a boxing operation happening there, but that is technically not the case. I'm unsure what the comparison of a box operation is vs. a call to TypedReference.InternalToObject; I assume it depends on the CLR implementation. Regardless, I believe the details are more or less correct now.


Solution:6

An enumeration type is distinct from an integer. In your sample, var does not evaluate to int, it evaluates to the enumeration type. You would get the same output if you had used the enumeration type itself.

Enumeration types output the name when printed, not their value.


Solution:7

The var value is actually an enum value (of type ReportStatus), so you see the standard behaviour of enumValue.ToString() - it's name.

EDIT:
When you do a Console.WriteLine(value.GetType()) you will see that it really is a 'ReportStatus', although it's boxed in a plain Object.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »