编辑:添加了一些示例代码,探索了许多(也许是全部?)遍历数组的可能方式。
默认情况下,枚举类型被认为是从 int“派生”的。如果需要,您可以选择从其他整数类型之一派生它,例如字节、短、长等。
在这两种情况下,对 Enum.GetValues 的调用都会返回一个 ReportStatus 对象数组。
在第一个循环中使用 var 关键字告诉编译器使用数组的指定类型 ReportStatus 来确定 value 变量的类型。枚举的 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 循环迭代指定数组的几种不同方法,包括详细描述每种情况下发生的情况的 cmets。
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);
}
}
}
-- 在上面的示例中更新了我的 cmets;经过仔细检查,我发现 System.Array.GetValue 方法实际上使用 TypedReference 类来提取数组的元素并将其作为 System.Object 返回。我最初写过那里发生了拳击操作,但从技术上讲并非如此。我不确定框操作与对 TypedReference.InternalToObject 的调用的比较是什么;我认为这取决于 CLR 实现。无论如何,我相信现在的细节或多或少是正确的。