为什么Enum.GetValues()在使用“var”时返回名称?

有谁能解释一下?

替代文字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 } } 

Enum.GetValues被声明为返回Array
它返回的数组包含实际值作为ReportStatus值。

因此, var关键字变为objectvalue变量保存(盒装)类型的枚举值。
Console.WriteLine调用解析为接受object并在对象上调用ToString()的重载,对于枚举,返回名称。

迭代int ,编译器会隐式地将值转换为int ,而value变量保存正常(和非盒装) int值。
因此, Console.WriteLine调用解析为带有int并打印它的重载。

如果将int更改为DateTime (或任何其他类型),它仍将编译,但它将在运行时抛出InvalidCastException

根据MSDN文档 , Console.WriteLine的重载采用object内部调用其参数的ToString

当你执行foreach (var value in ...) ,你的value变量被输入为object (因为, 当SLaks指出 , Enum.GetValues返回一个无类型的Array ),所以你的Console.WriteLine正在调用object.ToString ,它被覆盖通过System.Enum.ToString 。 此方法返回枚举的名称

当你执行foreach (int value in ...) ,你将枚举值转换为int值(而不是object ); 所以Console.WriteLine正在调用System.Int32.ToString

FWIW,这是来自Enum.GetValues()的反汇编代码(通过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; } 

看起来每个人都说var是一个object并调用object.ToString()返回名称是正确的...

当您使用Console.WriteLine时,您在每个元素上隐含地调用ToString()。

当你说你想要一个int(使用显式类型)时,它会将它转换为int – 然后转换为ToString()它。

第一个是Enum值ToString()’ed

编辑:添加了一些示例代码,探索了许多(可能所有?)可能的迭代数组的方法。

默认情况下,枚举类型被认为是从int“派生”的。 如果需要,您可以选择从其他整数类型派生它,例如byte,short,long等。

在这两种情况下,对Enum.GetValues的调用都返回一个ReportStatus对象数组。

在第一个循环中使用var关键字告诉编译器使用指定的数组类型(ReportStatus)来确定值变量的类型。 枚举的ToString实现是返回枚举条目的名称,而不是它所代表的整数值,这就是从第一个循环输出名称的原因。

在第二个循环中使用int变量会导致Enum.GetValues返回的值从ReportStatus隐式转换为int。 当然,在int上调用ToString将返回表示整数值的字符串。 隐式转换是导致行为差异的原因。

更新:正如其他人所指出的,Enum.GetValues函数返回一个类型为Array的对象,因此它是一个可枚举的Object类型,而不是ReportStatus类型。

无论如何,无论是迭代Array还是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); } } } 

为了一些额外的乐趣,我在下面添加了一些代码,演示了使用foreach循环迭代指定数组的几种不同方法,包括详细描述每种情况下发生的情况的注释。

 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); } } } 

– 更新了上述示例中的评论; 在仔细检查时,我看到System.Array.GetValue方法实际上使用TypedReference类来提取数组的元素并将其作为System.Object返回。 我原本写过那里发生过拳击行动,但技术上并非如此。 我不确定box操作与TypedReference.InternalToObject调用的比较是什么。 我认为它取决于CLR实现。 无论如何,我相信细节现在或多或少是正确的。

枚举类型与整数不同。 在您的示例中, var不计算为int,它计算为枚举类型。 如果您使用了枚举类型本身,则会获得相同的输出。

枚举类型在打印时输出名称,而不是它们的值。

var value实际上是一个枚举值(类型为ReportStatus),因此您可以看到enumValue.ToString()的标准行为 – 它的名称。

编辑:
当你执行Console.WriteLine(value.GetType())你会发现它确实是一个’ReportStatus’,尽管它是在一个普通的Object装箱的。