.NET枚举类型实际上是可变的值类型吗?

Jep*_*sen 17 .net c# powershell enums immutability

通过反射,在枚举类型的字段中查看,我惊讶地发现,保留枚举的特定实例的实际值的"支持"实例字段不是private,正如我所想的那样,但是public.它也不readonly是.(IsPublic是的,IsInitOnly假的.)

许多人认为.NET类型系统中的"可变"值类型"邪恶",那么为什么枚举类型(例如从C#代码创建)就是这样?

现在,事实证明,C#编译器有某种魔法否认公共实例字段的存在(但见下文),但在例如PowerShell中你可以这样做:

prompt> $d = [DayOfWeek]::Thursday
prompt> $d
Thursday
prompt> $d.value__ = 6
prompt> $d
Saturday
Run Code Online (Sandbox Code Playgroud)

该字段value__可以写入.

现在,要在C#中执行此操作,我必须使用,dynamic因为看起来使用正常的编译时成员绑定,C#假装public实例字段不存在.当然要使用dynamic,我们将不得不使用枚举值的装箱.

这是一个C#代码示例:

// create a single box for all of this example
Enum box = DayOfWeek.Thursday;

// add box to a hash set
var hs = new HashSet<Enum> { box, };

// make a dynamic reference to the same box
dynamic boxDyn = box;

// see and modify the public instance field
Console.WriteLine(boxDyn.value__);  // 4
boxDyn.value__ = 6;
Console.WriteLine(boxDyn.value__);  // 6 now

// write out box
Console.WriteLine(box);  // Saturday, not Thursday

// see if box can be found inside our hash set
Console.WriteLine(hs.Contains(box));  // False

// we know box is in there
Console.WriteLine(object.ReferenceEquals(hs.Single(), box));  // True
Run Code Online (Sandbox Code Playgroud)

我认为这些评论不言而喻.我们可以DayOfWeek通过public字段改变枚举类型的实例(可以是来自BCL程序集或来自"自制"程序集的任何枚举类型).由于实例位于哈希表中并且突变导致哈希码的更改,因此实例在突变之后处于错误的"桶"中,并且HashSet<>无法运行.

为什么.NET的设计者选择创建枚举类型的实例字段public?

Eri*_*ert 22

让我试着让那些不熟悉幕后产生枚举的读者理解这个相当令人困惑的问题.C#代码:

enum E { A, B }
Run Code Online (Sandbox Code Playgroud)

成为IL

.class private auto ansi sealed E extends [mscorlib]System.Enum
{
  .field public specialname rtspecialname int32 value__
  .field public static literal valuetype E A = int32(0x00000000)
  .field public static literal valuetype E B = int32(0x00000001)
} 
Run Code Online (Sandbox Code Playgroud)

或者,要再次在C#中重写,枚举等效于以下伪C#:

struct E : System.Enum
{
    public int value__;
    public const E A = 0;
    public const E B = 1;
}
Run Code Online (Sandbox Code Playgroud)

问题是:为什么神奇的领域是value__公开的?

我没有参与这个设计决定,所以我必须做出有根据的猜测.我受过教育的猜测是:如果字段不公开,如何初始化结构的实例?

你创建了一个构造函数,然后你必须调用它,这就是为抖动提供工作,这项工作的性能成本会给你带来什么?如果答案是"它给我买了运行时阻止我做一些愚蠢和危险的事情,我本来不应该做的事情,而且必须要努力工作"然后我向你提出这不是一个引人注目的成本效益比.

由于实例位于哈希表中并且突变导致哈希码的更改,因此实例在突变之后处于错误的"桶"中,并且哈希设置无法运行.

那距离"如果你这样做会伤害然后停止这样做 "就行了几英里.

  • 您的解释似乎很合理,但我认为这是不正确的。var d = DayOfWeek.Monday编译为ldc.i4.1; stloc.0`和(int)d编译为ldloc.0。所以该字段根本不会被访问:编译器生成的代码就像枚举与`int`一样。 (2认同)