仔细观察枚举类型的字段,我惊讶地发现保存枚举特定实例实际值的“支持”实例字段不是 private
,正如我所想的,但是public
.它不是 readonly
任何一个。 (IsPublic
真,IsInitOnly
假。)
许多人认为 .NET 类型系统中的“可变”值类型是“邪恶的”,那么为什么枚举类型(例如从 C# 代码创建的)就是这样?强>
现在,事实证明,C# 编译器有某种魔法可以否认公共(public)实例字段的存在(但见下文),但在例如PowerShell 你可以这样做:
prompt> $d = [DayOfWeek]::Thursday
prompt> $d
Thursday
prompt> $d.value__ = 6
prompt> $d
Saturday
字段value__
可以写入。
现在,要在 C# 中执行此操作,我必须使用 dynamic
因为看起来正常的编译时成员绑定(bind),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
我认为评论不言自明。我们可以改变枚举类型的实例 DayOfWeek
(可以是来自 BCL 程序集或来自“自制”程序集的任何枚举类型)到 public
field 。由于实例在哈希表中并且突变导致哈希码发生变化,因此实例在突变后位于错误的“桶”中,并且 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 E A = int32(0x00000000)
.field public static literal valuetype E B = int32(0x00000001)
}
或者,再次用 C# 重写,枚举等效于以下伪 C#:
struct E : System.Enum
{
public int value__;
public const E A = 0;
public const E B = 1;
}
问题是:为什么魔法字段value__
是公有的?
我没有参与这个设计决策,所以我必须做出有根据的猜测。我有根据的猜测是:如果该字段不公开,您如何初始化该结构的实例?
你创建了一个构造函数,然后你必须调用它,这给抖动带来了工作,而这项工作的性能成本会给你带来什么?如果答案是“它给我买了运行时间,防止我自己做一些我不应该做的愚蠢和危险的事情,而且必须非常努力地去做”那么我向你提出这不是一个令人信服的成本效益比。
Since the instance was in a hashtable and the mutation lead to a change of hash code, the instance is in the wrong "bucket" after the mutation, and the HashSet cannot function.
这比“如果你这样做时感到疼痛,那么停止这样做”这一行已经过了好几英里。
关于c# - .NET 枚举类型实际上是可变值类型吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17891275/