17

列挙型のフィールドを熟考して見てみると、驚いたことに、列挙型の特定のインスタンスの実際の値を保持する「バッキング」インスタンス フィールドはprivate、私が思っていたように ではなく、 であることに気付きましたpublic。そして、そうではありませんでしreadonlyた。(IsPublic真、IsInitOnly偽。)

多くの人が .NET 型システムの「変更可能な」値型を「悪」と考えていますが、なぜ列挙型(たとえば C# コードから作成されたもの)がそれだけなのでしょうか?

Now, as it turns out, the C# compiler has some kind of magic that denies the existence of the public instance field (but see below), but in e.g. PowerShell you can do this:

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

The field value__ can be written to.

Now, to do this in C#, I had to use dynamic because it seems that with the normal compile-time member binding, C# pretends that the public instance field does not exist. Of course to use dynamic, we will have to use boxing of the enum value.

Here's a C# code example:

// 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

I think the comments speak for themselves. We can mutate an instance of the enum type DayOfWeek (could be any enum type from a BCL assembly or from a "home-made" assembly) through a public field. 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.

Why did the designers of .NET choose to make the instance field of enum types 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__公開されているのかということです。

私はこの設計上の決定に関与していなかったので、知識に基づいた推測を行う必要がありました. 私の経験に基づいた推測は次のとおりです。フィールドがパブリックでない場合、構造体のインスタンスをどのように初期化しますか?

コンストラクターを作成し、それを呼び出す必要があります。これにより、ジッターに仕事が与えられます。その仕事のパフォーマンス コストはいくらになるでしょうか? 答えが「最初からやるべきではなく、本当に一生懸命働かなければならなかった愚かで危険なことをするのを防ぐためのランタイムを買うことができます」である場合、私はこれが説得力のある費用対効果の比率。

インスタンスはハッシュテーブルにあり、ミューテーションによってハッシュコードが変更されるため、ミューテーション後にインスタンスは間違った「バケット」にあり、HashSet は機能しません。

それは、「それをすると痛い場合はやめてください」という線を数マイル過ぎたところです。

于 2013-07-26T21:57:26.387 に答える