C# 为什么Enum.GetValues()在使用";时返回名称;var";?

C# 为什么Enum.GetValues()在使用";时返回名称;var";?,c#,enums,anonymous-types,C#,Enums,Anonymous Types,有人能解释一下吗 Enum.GetValues声明为返回Array 它返回的数组包含实际值,即ReportStatus值 因此,var关键字变为对象,而值变量保存(装箱)类型的枚举值。 Console.WriteLine调用解析为重载,该重载接受对象并对该对象调用ToString(),对于枚举,该对象返回名称 当您在int上迭代时,编译器会隐式地将值强制转换为int,并且value变量保持正常(和非装箱)的int值。 因此,Console.WriteLine调用解析为重载,该重载接受一个int

有人能解释一下吗


Enum.GetValues
声明为返回
Array

它返回的数组包含实际值,即
ReportStatus

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

当您在
int
上迭代时,编译器会隐式地将值强制转换为
int
,并且
value
变量保持正常(和非装箱)的
int
值。
因此,
Console.WriteLine
调用解析为重载,该重载接受一个
int
并打印它

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

根据,控制台的重载。接受
对象的WriteLine
在内部调用
ToString

当您执行
foreach(var value in…
)时,您的
变量被键入为
对象
(因为,返回一个未键入的
数组
),因此您的
控制台.WriteLine
正在调用
对象.ToString
,它被
系统.Enum.ToString
覆盖。此方法返回枚举的名称


当您执行
foreach(intvalue in…
)时,您正在将枚举值强制转换为
int
值(而不是
object
);因此,
Console.WriteLine
正在调用
System.Int32.ToString
当您使用Console.WriteLine时,会隐式地调用每个元素上的ToString()

当您说需要一个int(使用显式类型)时,它会将其转换为int,然后将其转换为ToString()


第一个是枚举值ToString()'ed

枚举类型不同于整数。在您的示例中,
var
的计算结果不是int,而是枚举类型。如果使用枚举类型本身,则会得到相同的输出


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

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

编辑:

当您执行
控制台.WriteLine(value.GetType())
时,您将看到它实际上是一个“ReportStatus”,尽管它被装箱在一个普通的
对象中

FWIW,下面是从Enum.GetValues()中(通过反射镜)反汇编的代码:

[ComVisible(true)]
公共静态数组GetValues(类型enumType)
{
if(enumType==null)
{
抛出新ArgumentNullException(“enumType”);
}
如果(!(enumType为RuntimeType))
{
抛出新的ArgumentException(Environment.GetResourceString(“Arg_MustBeType”),“enumType”);
}
如果(!enumType.IsEnum)
{
抛出新的ArgumentException(Environment.GetResourceString(“Arg_MustBeEnum”),“enumType”);
}
ulong[]value=GetHashEntry(enumType).value;
Array Array=Array.CreateInstance(enumType,values.Length);
for(int i=0;i

看起来就像大家所说的那样,
var
是一个
对象
并调用
object.ToString()
返回名称是正确的…

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

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

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

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

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

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

无论如何,无论是在数组上迭代还是在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
上时,Visual Studio会说类型是什么?不知道,但请参见
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);
        }
    }
}
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);
        }
    }
}