通过反思,在枚举类型的字段中,我惊讶地发现,保留枚举的特定实例的实际值的“支持”实例字段不是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
字段来改变枚举类型public
的实例(可以是来自BCL程序集或来自“自制”程序集的任何枚举类型)。由于实例位于哈希表中并且突变导致哈希代码更改,因此实例在突变后位于错误的“桶”中,HashSet<>
无法运行。
为什么.NET的设计者选择创建枚举类型public
的实例字段?
答案 0 :(得分:21)
让我试着让那些不熟悉幕后产生枚举的读者理解这个相当混乱的问题。 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__
是公开的?
我不在乎这个设计决定,所以我必须做出有根据的猜测。我受过教育的猜测是:如果字段不公开,如何初始化结构的实例?
你创建了一个构造函数,然后你必须调用它,这就是为抖动提供工作,这项工作的性能成本会给你带来什么?如果答案是“它给我买了运行时阻止我做一些愚蠢和危险的事情,我本来不应该做的事情,而且必须要努力工作”然后我向你提出这不是一个令人信服的成本效益比。
由于实例位于哈希表中并且突变导致哈希码的更改,因此实例在突变后位于错误的“桶”中,并且哈希设置无法运行。
距离“如果你这样做会伤害停止那样做”就行了几英里。