枚举从System.Enum派生,同时是一个整数吗? [英] How is it that an enum derives from System.Enum and is an integer at the same time?

查看:157
本文介绍了枚举从System.Enum派生,同时是一个整数吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑:底部的评论。另外,这个

Edit: Comments at bottom. Also, this.

有什么让我感到困惑我的理解是,如果我有这样的枚举...

Here's what's kind of confusing me. My understanding is that if I have an enum like this...

enum Animal
{
    Dog,
    Cat
}

...我基本做的是使用两个定义的值定义了一个名为 Animal 值类型 Dog 。这种类型源自引用类型 System.Enum (某些值类型通常不能执行的操作 - 至少不是在C#中,而是在这种情况下允许),并且具有来自/ int 值之间来回转换的功能。

...what I've essentially done is defined a value type called Animal with two defined values, Dog and Cat. This type derives from the reference type System.Enum (something which value types can't normally do—at least not in C#—but which is permitted in this case), and has a facility for casting back and forth to/from int values.

如果我刚刚描述了上面的枚举类型的方式是true,那么我会期望以下代码抛出一个 InvalidCastException

If the way I just described the enum type above were true, then I would expect the following code to throw an 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);
    }
}

通常,您无法从 System.Object 除了其确切类型之外那么上述可能如何?就好像 Animal type 一个 int (不只是可转换 c c > 可兑换枚举)。 是多重继承吗? System.Enum 以某种方式继承自 System.Int32 (我不会有预期的可能)罢工>

Normally, you cannot unbox a value type from System.Object as anything other than its exact type. So how is the above possible? It is as if the Animal type is an int (not just convertible to int) and is an Enum (not just convertible to Enum) at the same time. Is it multiple inheritance? Does System.Enum somehow inherit from System.Int32 (something I would not have expected to be possible)?

修改:不能是上述任一项。以下代码显示了(我认为)最终结论:

Edit: It can't be either of the above. The following code demonstrates this (I think) conclusively:

object animal = Animal.Dog;

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

上述输出:

True
False

关于枚举的MSDN文档和C#规范使用术语底层类型;但我不知道这是什么意思,也没有听说过它用于引用除枚举之外的其他内容。 底层类型实际上是什么意思?

Both the MSDN documentation on enumerations and the C# specification make use of the term "underlying type"; but I don't know what this means, nor have I ever heard it used in reference to anything other than enums. What does "underlying type" actually mean?

所以,这是另一个从CLR获得特殊待遇的案例

我的钱在这种情况下...但答案/解释将是不错的。

My money's on that being the case... but an answer/explanation would be nice.

更新 Damien_The_Unbeliever 提供了真正回答这个问题的参考。解释可以在CLI规范的分区II的枚举部分中找到:

Update: Damien_The_Unbeliever provided the reference to truly answer this question. The explanation can be found in Partition II of the CLI specification, in the section on enums:


对于绑定目的(例如,
从用于调用它的
方法引用中定义方法定义)
枚举与其
底层类型不同。对于所有其他
目的,包括验证和
执行代码,一个未装箱的枚举
可以自由地互换其
底层类型
。枚举可以在相应的盒装实例
类型的框中输入
,但是这种类型不是与
类型的底层类型相同的
,所以拳击不会丢失
原始类型的枚举。

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%解释专门的拆箱行为本身(虽然我正在离开Damien的答案被接受,因为它在这个问题上散发了很多光)。我会继续研究这个...

Edit (again?!): Wait, actually, I don't know that I read that right the first time. Maybe it doesn't 100% explain the specialized unboxing behavior itself (though I'm leaving Damien's answer as accepted, as it shed a great deal of light on this issue). I will continue looking into this...

另一个编辑:Man,then yodaj007的回答扔给我另一个循环。不知何故,枚举与 int 不完全一样;但是,可以将 int 分配给没有转换的枚举变量 ? Buh?

Another Edit: Man, then yodaj007's answer threw me for another loop. Somehow an enum is not exactly the same as an int; yet an int can be assigned to an enum variable with no cast? Buh?

我认为这一切都是最终被汉斯的答案,这就是为什么我接受了它。 (抱歉,Damien!)

I think this is all ultimately illuminated by Hans's answer, which is why I've accepted it. (Sorry, Damien!)

推荐答案

是的,特别待遇。 JIT编译器非常了解盒装值类型的工作方式。这通常是什么使价值类型发生了一些分裂。 Boxing涉及创建一个System.Object值,其行为与引用类型的值完全相同。在这一点上,值类型值不再像运行时的​​值那样表现。这使得例如有一个像ToString()的虚拟方法成为可能。盒子对象有一个方法表指针,就像引用类型一样。

Yes, special treatment. The JIT compiler is keenly aware of the way boxed value types work. Which is in general what makes value types acting a bit schizoid. Boxing involves creating a System.Object value that behaves exactly the same way as a value of a reference type. At that point, value type values no longer behave like values do at runtime. Which makes it possible, for example, to have a virtual method like ToString(). The boxed object has a method table pointer, just like reference types do.

JIT编译器知道方法表指针,像int和bool之类的值类型。对于他们的拳击和拆箱是非常有效的,它只需要少量的机器代码指令。这需要在.NET 1.0中有效地使其具有竞争力。一个非常重要的部分是限制值类型值只能被取消装箱到相同的类型。这避免了抖动不得不生成调用正确转换代码的大量开关语句。它所要做的就是检查对象中的方法表指针,并确认它是预期的类型。并直接从对象中复制值。值得注意的是,这种限制在VB.NET中不存在,它的CType()操作符实际上生成一个包含这个大switch语句的帮助函数的代码。

The JIT compiler knows the method tables pointers for value types like int and bool up front. Boxing and unboxing for them is very efficient, it takes but a handful of machine code instructions. This needed to be efficient back in .NET 1.0 to make it competitive. A very important part of that is the restriction that a value type value can only be unboxed to the same type. This avoids the jitter from having to generate a massive switch statement that invokes the correct conversion code. All it has to do is to check the method table pointer in the object and verify that it is the expected type. And copy the value out of the object directly. Notable perhaps is that this restriction doesn't exist in VB.NET, its CType() operator does in fact generate code to a helper function that contains this big switch statement.

枚举类型的问题是这不行。枚举可以具有不同的GetUnderlyingType()类型。换句话说,未装箱的值具有不同的大小,所以简单地将值从盒装的对象复制起来是不行的。敏锐地意识到,抖动不再包围拆箱代码,它会在CLR中产生一个调用函数的帮助。

The problem with Enum types is that this cannot work. Enums can have a different GetUnderlyingType() type. In other words, the unboxed value has different sizes so simply copying the value out of the boxed object cannot work. Keenly aware, the jitter doesn't inline the unboxing code anymore, it generates a call to a helper function in the CLR.

该帮助器名为JIT_Unbox(),你可以在SSCLI20源代码中找到源代码,clr / src / vm / jithelpers.cpp。你会看到它特别处理枚举类型。它是宽容的,它允许从一个枚举类型到另一个枚举类型。但是,只有底层类型相同,如果不是这样,你会得到一个InvalidCastException。

That helper is named JIT_Unbox(), you can find its source code in the SSCLI20 source, clr/src/vm/jithelpers.cpp. You'll see it dealing with enum types specially. It is permissive, it allows unboxing from one enum type to another. But only if the underlying type is the same, you get an InvalidCastException if that's not the case.

这也是将枚举声明为类的原因。它的逻辑行为是一种引用类型,派生的枚举类型可以从一个转换到另一个。上述对基础类型兼容性的限制。枚举类型的值非常多,值类型值的行为。他们有复制语义和拳击行为。

Which is also the reason that Enum is declared as a class. Its logical behavior is of a reference type, derived enum types can be cast from one to another. With the above noted restriction on the underlying type compatibility. The values of an enum type have however very much the behavior of a value type value. They have copy semantics and boxing behavior.

这篇关于枚举从System.Enum派生,同时是一个整数吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆