РЕДАКТИРОВАТЬ: добавлен пример кода, который исследует много (возможно, все?) Возможных способов перебора массива.
Типы перечислений по умолчанию считаются «производными» от int. При желании вы можете получить его из одного из целочисленных типов, таких как байты, короткие, длинные и т. Д.
В обоих случаях вызов Enum.GetValues
возвращает массив объектов ReportStatus.
Использование ключевого слова var в первом цикле указывает компилятору использовать указанный тип массива, а именно ReportStatus, для определения типа переменной-значения. Реализация ToString для перечислений должна возвращать имя записи перечисления, а не целочисленное значение, которое она представляет, поэтому имена выводятся из первого цикла.
Использование переменной int во втором цикле приводит к неявному преобразованию значений, возвращаемых Enum.GetValues
, из ReportStatus в int. Вызов ToString для int, конечно, вернет строку, представляющую целочисленное значение. Неявное преобразование - это то, что вызывает разницу в поведении.
ОБНОВЛЕНИЕ: Как уже отмечали другие, функция 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. Несмотря на это, я считаю, что сейчас детали более или менее верны.