Рядом с выбором «Регулярное выражение» в «режиме поиска» установлен флажок «match newline» (согласно @glatapoui, следует отметить, что это работает только в Notepad ++ v6, а не в предыдущих версиях).
[/g0]
Enum.GetValues
объявлен как возвращающий массив
.
Возвращаемый им массив содержит фактические значения в виде значений ReportStatus
.
Таким образом, ключевое слово var
становится объектом
, а переменная value
содержит (в рамке) типизированные значения перечисления.
Вызов Console.WriteLine
преобразуется в перегрузку, которая принимает объект
и вызывает ToString ()
для объекта, который для перечислений возвращает имя.
Когда вы перебираете int
, компилятор неявно преобразует значения в int
, а переменная value
остается нормальной (и без упаковки) int
значения.
Следовательно, вызов Console.WriteLine
разрешается в перегрузку, которая принимает int
и печатает его.
Если вы измените int
на DateTime
(или любой другой тип), он все равно будет компилироваться, но во время выполнения вызовет исключение InvalidCastException
.
Тип перечисления отличается от целого числа. В вашем примере var
не оценивает int, оно оценивает тип перечисления. Вы бы получили тот же результат, если бы использовали сам тип перечисления.
Типы перечисления выводят имя при печати, а не их значение.
var value
на самом деле является значением enum (типа ReportStatus), поэтому вы видите стандартное поведение enumValue.ToString () - это имя.
РЕДАКТИРОВАТЬ:
Когда вы сделаете Console.WriteLine(value.GetType())
, вы увидите, что это действительно «ReportStatus», хотя оно упаковано в простой Object
.
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.ToString ()
возвращает правильное имя ...
Вы неявно вызываете ToString() для каждого элемента, когда используете Console.WriteLine.
И когда вы говорите, что вам нужен int (используя явный тип), он преобразует его в int - а затем ToString() его.
Первое - значение Enum ToString()'ed
EDIT: добавлен пример кода, который исследует многие (возможно, все?) возможные способы итерации по массиву.
Типы Enum по умолчанию считаются "производными" от int. Вы можете выбрать производное от одного из других целочисленных типов, таких как byte, short, long и т.д.
В обоих случаях вызов Enum.GetValues
возвращает массив объектов ReportStatus.
Использование ключевого слова var в первом цикле указывает компилятору использовать указанный тип массива, который является ReportStatus, для определения типа переменной value. Реализация ToString для перечислений должна возвращать имя элемента перечисления, а не целочисленное значение, которое он представляет, вот почему имена выводятся из первого цикла.
Использование переменной int во втором цикле приводит к тому, что значения, возвращаемые Enum.GetValues
, неявно преобразуются из ReportStatus в int. Вызов ToString для int, конечно же, вернет строку, представляющую целочисленное значение. Неявное преобразование и является причиной разницы в поведении.
UPDATE: Как отметили другие, функция 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. Изначально я написал, что там происходит операция боксирования, но технически это не так. Я не знаю, чем отличается операция боксирования от вызова TypedReference.InternalToObject; я предполагаю, что это зависит от реализации CLR. Независимо от этого, я считаю, что детали теперь более или менее правильные.
Согласно документации MSDN , перегрузка Console.WriteLine
, которая принимает объект ,
внутренне вызывает ToString
о своем аргументе.
Когда вы выполняете foreach (значение переменной в ...)
, ваша переменная value
набирается как объект
(поскольку, как точки SLaks out , Enum.GetValues
возвращает нетипизированный массив
), и поэтому ваш Console.WriteLine
вызывает object.ToString
, который является переопределено System.Enum.ToString
. И этот метод возвращает имя перечисления.
Когда вы выполняете foreach (int value in ...)
, вы преобразуете значения перечисления в значения int
(вместо object
); поэтому Console.WriteLine
вызывает System.Int32.ToString
.