.net - 为什么枚举派生自 System.Enum 并且同时是整数?

标签 .net inheritance enums clr unboxing

编辑:评论在底部。另外,this .

<小时/>

这就是让我感到困惑的地方。我的理解是,如果我有这样的枚举......

enum Animal
{
    Dog,
    Cat
}

...我本质上所做的是定义一个名为 Animal值类型,其中包含两个定义的值:Dog。此类型派生自引用类型 System.Enum(值类型通常无法做到这一点——至少在 C# 中不能做到——但在本例中是允许的),并且具有在 int 值之间来回转换的功能。

如果我上面描述的枚举类型的方式是正确的,那么我希望以下代码抛出 InvalidCastException:

public class Program
{
    public static void Main(string[] args)
    {
        // Box it.
        object animal = Animal.Dog;

        // Unbox it. How are these both successful?
        int i = (int)animal;
        Enum e = (Enum)animal;

        // Prints "0".
        Console.WriteLine(i);

        // Prints "Dog".
        Console.WriteLine(e);
    }
}

通常,you cannot unbox a value type from System.Object as anything other than its exact type .那么上面的情况怎么可能呢?就好像 Animal 类型一个int(不仅仅是可转换int)并且同时一个Enum(不仅仅是可转换Enum)。 是多重继承吗? System.Enum 是否以某种方式从 System.Int32 继承(这是我没想到的)?

编辑:不能是以上任何一种。以下代码最终证明了这一点(我认为):

object animal = Animal.Dog;

Console.WriteLine(animal is Enum);
Console.WriteLine(animal is int);

以上输出:

True
False

两者the MSDN documentation on enumerations C# 规范使用术语“底层类型”;但我不知道这意味着什么,也没有听说过它用于引用枚举以外的任何东西。 “底层类型”实际上是什么意思?

<小时/>

那么,这又是一个 case that gets special treatment from the CLR

我的钱是在这种情况下......但答案/解释会很好。

<小时/>

更新:Damien_The_Unbeliever为真正回答这个问题提供了引用。可以在 CLI 规范的第二部分中有关枚举的部分中找到说明:

For binding purposes (e.g., for locating a method definition from the method reference used to call it) enums shall be distinct from their underlying type. For all other purposes, including verification and execution of code, an unboxed enum freely interconverts with its underlying type. Enums can be boxed to a corresponding boxed instance type, but this type is not the same as the boxed type of the underlying type, so boxing does not lose the original type of the enum.

编辑(再次?!):等等,实际上,我不知道我第一次读的是否正确。也许它并不能 100% 解释专门的拆箱行为本身(尽管我接受达米安的答案,因为它为这个问题提供了很多线索)。我将继续调查这个问题...

<小时/>

另一个编辑:伙计,然后 yodaj007's answer让我又循环了一次。不知何故,枚举与int并不完全相同;然而,int 可以分配给一个枚举变量而不需要强制转换吗?嗯?

我认为这一切最终都被 Hans's answer 所阐明。 ,这就是我接受它的原因。 (对不起,达米安!)

最佳答案

是的,特殊待遇。 JIT 编译器敏锐地意识到装箱值类型的工作方式。一般来说,这就是导致值类型表现得有点 split 的原因。装箱涉及创建一个 System.Object 值,其行为方式与引用类型的值完全相同。此时,值类型值的行为不再像运行时的​​值一样。例如,这使得拥有像 ToString() 这样的虚拟方法成为可能。装箱对象有一个方法表指针,就像引用类型一样。

JIT 编译器预先知道 int 和 bool 等值类型的方法表指针。对它们进行装箱和拆箱非常高效,只需要少量机器代码指令。这需要在 .NET 1.0 中保持高效,以使其具有竞争力。其中一个非常重要的部分是值类型值只能拆箱为相同类型的限制。这避免了必须生成调用正确转换代码的大量 switch 语句而产生的抖动。它所要做的就是检查对象中的方法表指针并验证它是否是预期的类型。并直接将值复制出对象。值得注意的是,VB.NET 中不存在此限制,它的 CType() 运算符实际上会为包含此大 switch 语句的辅助函数生成代码。

枚举类型的问题是它无法工作。枚举可以有不同的 GetUnderlyingType() 类型。换句话说,未装箱的值具有不同的大小,因此简单地从装箱的对象中复制值是行不通的。敏锐地意识到,抖动不再内联拆箱代码,它会生成对 CLR 中辅助函数的调用。

该帮助程序名为 JIT_Unbox(),您可以在 SSCLI20 源 clr/src/vm/jithelpers.cpp 中找到其源代码。您会看到它专门处理枚举类型。它是宽松的,它允许从一种枚举类型拆箱到另一种枚举类型。但只有当基础类型相同时,您才会收到 InvalidCastException,否则就会出现 InvalidCastException。

这也是 Enum 被声明为类的原因。它的逻辑行为是引用类型,派生枚举类型可以从一种类型转换为另一种类型。具有上述对底层类型兼容性的限制。然而,枚举类型的值具有与值类型值非常相似的行为。它们具有复制语义和装箱行为。

关于.net - 为什么枚举派生自 System.Enum 并且同时是整数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4626394/

相关文章:

Java - 强制继承 N 个重写抽象方法中的 1 个

c++ - 半私有(private)枚举值

python - Python中Enum和IntEnum之间的区别

c# - 在另一个列表中的列表中查找项目?

c++ - 为什么 pA、pB、pC 不相等?

c# - 无法安装 .NET 框架,因为 Windows 认为安装了更新版本

java - java开源框架中编辑一个类的最佳方式

c# - 查看枚举是否包含标志的通用扩展方法

.net - 如何获得受信任/经过验证的发布者?

c# - .NET : How to find the source code for a line of code using the exe and pdb file