它是如何枚举派生自System.Enum,是在同一时间的整数?是在、它是、整数、Enum

2023-09-03 08:37:13 作者:←長得太帥忚四①種檌√

修改:评论底部。此外,this.

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
}

...我已经基本上做的是定义一个值类型的名为动物有两个定义的值,。从引用类型的 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.

如果我只是上述枚举类型的方式是真的,那么我希望下面的code抛出 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);
    }
}

通常情况下,you不能拆箱从 System.Object的的值类型以外的任何其他的确切类型。那么,如何在上述可能吗?这是因为,如果动物键入是的 INT (不只是的兑换到 INT )和是的枚举(不只是的转换到枚举)在同一时间。 它是多重继承?请问 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)?

修改:它不能上述任一。下面code说明了这一点(我认为)得出结论:

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?

所以,这是又一个case那得到特殊待遇从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:

有关结合目的(例如,用于   定位从一个方法定义   习惯称它为方法参考)   枚举应从不同的自己   基础类型。对于所有其它   目的,包括验证和   执行code,的拆箱枚举   与自由interconverts其   基础类型。枚举可以盒装   到相应的包装实例   类型,但这种类型的没有的同   作为盒装类型的底层的   类型,所以拳击不输   原始类型的枚举。

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%说明专业拆箱行为本身(虽然我要离开达米安的答案被接受,因为它洒下大量的光在这个问题上)。我将继续寻找到这...

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...

另一个修改:男人,那么yodaj007's回答把我的另一个循环。不知怎的,一个枚举是不完全一样的 INT ;又一个 INT 可以被分配给一个枚举变量的没有投的?寮步?

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?

我觉得这一切最终由Hans's回答,这就是为什么我已经接受了它。 (不好意思,达明!)

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

推荐答案

是的,特殊处理。 JIT编译器是敏锐地觉察的方式装箱值类型的工作。这是一般什么使值类型演戏有点精神分裂。拳击包括创建一个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的锋线。装箱和拆箱对他们来说是的非常的效率,它需要但少数机器code指令。这需要有效率回.NET 1.0,使其具有竞争力。 A 非常的这一重要部分是限制,即是值类型的值只能是拆箱同一类型。这就避免了抖动不必生成调用正确的转换code一个巨大的switch语句。它所需要做的就是检查方法表指针的对象,并确认其预期的类型。并复制值了直接的对象。值得注意的或许是,这种限制并不在VB.NET中存在,它的CTYPE()操作者实际上产生code到一个辅助功能,它包含这个大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()的类型。换句话说,在未装箱的值具有不同的尺寸所以简单地复制数值超出盒装对象无法正常工作。敏锐地意识到,抖动不内联拆箱code了,它会生成一个调用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(),你可以找到它的源$ C ​​$ c在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.