2013-07-26 7 views
15

Mit Nachdenken, in den Feldern eines Enum-Typs, bemerkte ich zu meiner Überraschung, dass das "Backing" -Instanzfeld, das den tatsächlichen Wert einer bestimmten Instanz des Enums enthält, nicht private ist, wie ich gedacht hätte, aber public. Und es war auch nicht readonly. (IsPublic wahr, IsInitOnly false.)Sind .NET-Enum-Typen tatsächlich veränderbare Wertetypen?

Viele Menschen „wandelbar“ Wertetypen in .NET-Typ-System „Übel“ betrachten, so warum sind die Aufzählungstypen (von C# -Code zum Beispiel erstellt) nur, dass ?

Nun, wie sich herausstellt, hat der C# -Compiler eine Art von Magie, die die Existenz des öffentlichen Instanzfeldes bestreitet (aber siehe unten), aber z. Powershell Sie können dies tun:

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

Das Feld value__ geschrieben werden kann.

Nun, um dies in C# zu tun, musste ich dynamic verwenden, weil es scheint, dass mit der normalen Compilierungszeit Memberbindung C# vorgibt, dass die public Instanz Feld nicht existiert. Um dynamic zu verwenden, müssen wir boxen des Enum-Werts verwenden.

Hier ist ein C# Codebeispiel:

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

Ich denke, die Kommentare sprechen für sich. Wir können eine Instanz des Aufzählungstyps DayOfWeek (kann ein beliebiger Aufzählungstyp von einer BCL-Assembly oder von einer "hausgemachten" Assembly sein) durch ein public Feld mutieren. Da die Instanz in einer Hashtabelle war und die Mutation zu einer Änderung des Hash-Codes führte, befindet sich die Instanz nach der Mutation im falschen "Bucket" und die HashSet<> kann nicht funktionieren.

Warum haben die Designer von .NET entschieden, das Instanzfeld von Enum-Typen public zu machen?

+0

Ich frage mich über Sie Ideen, was wäre eine ideale Umsetzung. Bist du bereit zu teilen? –

+2

Eigentlich gibt es einen viel einfacheren Weg zu beweisen, dass Enum-Typen veränderbar sind, ohne Boxen oder dynamisch: https://gist.github.com/thomaslevesque/6100447 –

+0

@KenKin Nun, vor allem ist meine Idee, dass die Instanz Feld das hält den tatsächlichen Wert der aktuellen Instanz sollte "privat" sein. Ob der Name "witzige" Zeichen enthält (Zeichen, die in C# nicht in identischen Namen vorkommen) oder nicht, wäre meiner Meinung nach weniger wichtig. –

Antwort

20

Lassen Sie mich versuchen, diese ziemlich verwirrende Frage für Leser zu verstehen, die nicht damit vertraut sind, wie enums hinter den Kulissen erzeugt werden. Der C# Code:

enum E { A, B } 

wird die 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) 
} 

Oder neu zu schreiben, das wieder in C#, die ENUM entspricht die folgende Pseudo-C#:

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

Die Frage ist: warum ist das magische Feld value__ öffentlich?

Ich war nicht für diese Designentscheidung, also musste ich eine begründete Vermutung abgeben. Meine fundierte Vermutung wäre: Wie initialisierst du eine Instanz der Struktur, wenn das Feld nicht öffentlich ist?

Sie machen einen Konstruktor, den Sie dann aufrufen müssen, und das gibt dem Jitter Arbeit, und was kostet Sie die Leistung dieser Arbeit? Wenn die Antwort lautet: "Es kauft mir die Laufzeit, die mich daran hindert, etwas Dummes und Gefährliches zu tun, was ich eigentlich gar nicht tun sollte und sehr hart daran arbeiten musste", dann unterwerfe ich Ihnen, dass dies keine ist überzeugendes Kosten-Nutzen-Verhältnis.

Da die Instanz in einer Hashtabelle war und die Mutation zu einer Änderung des Hash-Codes führte, befindet sich die Instanz nach der Mutation im falschen "Bucket" und das HashSet kann nicht funktionieren.

Das ist einige Meilen hinter der „wenn es weh tut, wenn Sie das tun, dann stoppen, die tun“ -Zeile.

+0

Ich weiß nicht genug darüber, um zu sagen, ob es einen Preis für die Leistung eines Einzellinienkonstrukteurs geben würde, aber ich denke, Sie haben recht, die Erklärung ist leistungsbezogen. Das Design ist nur erfolgreich, denke ich, wegen dieser Magie im C# Compiler, die es das öffentliche Feld nicht "sieht". Wenn "Wert__" in Intellisense auftaucht und geschrieben werden könnte (ohne "wirklich hart arbeiten" zu müssen, indem man "dynamisch" verwendet), würden viele Leute in dieses Feld schreiben, und das würde die "praktische" Semantik von Enums ändern . –

+0

Und es ist ein wenig seltsam, dass die Compile-Time-Bindung mit nicht-dynamischen Typen das 'value__'-Feld ignoriert, während die Laufzeit-Bindung mit' dynamic' dieses Feld auswählt und verwendet, ohne zu werfen. –

+0

Das letzte Bit scheint mir ein Fehler. –

Verwandte Themen