Почему делает Перечисление. GetValues () возвращают имена при использовании “var”?

Рядом с выбором «Регулярное выражение» в «режиме поиска» установлен флажок «match newline» (согласно @glatapoui, следует отметить, что это работает только в Notepad ++ v6, а не в предыдущих версиях).

enter image description here [/g0]

28
задан Edward Tanguay 9 July 2010 в 14:13
поделиться

7 ответов

Enum.GetValues ​​ объявлен как возвращающий массив .
Возвращаемый им массив содержит фактические значения в виде значений ReportStatus .

Таким образом, ключевое слово var становится объектом , а переменная value содержит (в рамке) типизированные значения перечисления.
Вызов Console.WriteLine преобразуется в перегрузку, которая принимает объект и вызывает ToString () для объекта, который для перечислений возвращает имя.

Когда вы перебираете int , компилятор неявно преобразует значения в int , а переменная value остается нормальной (и без упаковки) int значения.
Следовательно, вызов Console.WriteLine разрешается в перегрузку, которая принимает int и печатает его.

Если вы измените int на DateTime (или любой другой тип), он все равно будет компилироваться, но во время выполнения вызовет исключение InvalidCastException .

42
ответ дан 28 November 2019 в 03:09
поделиться

Тип перечисления отличается от целого числа. В вашем примере var не оценивает int, оно оценивает тип перечисления. Вы бы получили тот же результат, если бы использовали сам тип перечисления.

Типы перечисления выводят имя при печати, а не их значение.

0
ответ дан Shirik 28 November 2019 в 03:09
поделиться

var value на самом деле является значением enum (типа ReportStatus), поэтому вы видите стандартное поведение enumValue.ToString () - это имя.

РЕДАКТИРОВАТЬ:
Когда вы сделаете Console.WriteLine(value.GetType()), вы увидите, что это действительно «ReportStatus», хотя оно упаковано в простой Object.

0
ответ дан Hans Kesting 28 November 2019 в 03:09
поделиться

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 () возвращает правильное имя ...

3
ответ дан 28 November 2019 в 03:09
поделиться

Вы неявно вызываете ToString() для каждого элемента, когда используете Console.WriteLine.

И когда вы говорите, что вам нужен int (используя явный тип), он преобразует его в int - а затем ToString() его.

Первое - значение Enum ToString()'ed

2
ответ дан 28 November 2019 в 03:09
поделиться

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. Независимо от этого, я считаю, что детали теперь более или менее правильные.

1
ответ дан 28 November 2019 в 03:09
поделиться

Согласно документации 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 .

5
ответ дан 28 November 2019 в 03:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: