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

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

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

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

 prompt> $d = [DayOfWeek]::Thursday prompt> $d Thursday prompt> $d.value__ = 6 prompt> $d Saturday 

字段值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 { 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 

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

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

让我试着为那些不熟悉幕后产生枚举的读者理解这个相当混乱的问题。 C#代码:

 enum E { A, B } 

成为IL

 .class private auto ansi sealed E extends [mscorlib]System.Enum { .field public specialname rtspecialname int32 value__ .field public static literal valuetype EA = int32(0x00000000) .field public static literal valuetype EB = int32(0x00000001) } 

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

 struct E : System.Enum { public int value__; public const EA = 0; public const EB = 1; } 

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

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

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

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

那是几英里之后的“如果你这样做会伤害然后停止这样做 ”。