17

通过反思,在枚举类型的字段中,我惊讶地注意到保存枚举的特定实例的实际值的“支持”实例字段不是private,正如我所想的那样,而是public。它也不readonly是。(IsPublic对,IsInitOnly错。)

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

现在,事实证明,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<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。由于实例在哈希表中,并且变异导致哈希码发生变化,变异后实例在错误的“桶”中,HashSet<>无法运行。

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

4

1 回答 1

22

对于那些不熟悉枚举是如何在幕后生成的读者,让我试着理解这个相当令人困惑的问题。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__公开的?

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

你创建了一个构造函数,然后你必须调用它,这为抖动提供了工作,而这项工作的性能成本给你带来了什么?如果答案是“它为我赢得了运行时间,防止自己做一些我一开始就不应该做的愚蠢和危险的事情,而且我必须非常努力地去做”,那么我向你表明这不是一个令人信服的成本效益比。

由于实例在hashtable中,mutation导致hash码发生变化,导致实例在mutation后在错误的“bucket”中,HashSet无法正常工作。

这比“如果你这样做时感到疼痛,那就停止这样做”这条线已经过了几英里。

于 2013-07-26T21:57:26.387 回答