2013-07-26 10 views
15

Patrząc, z odbiciem, na polach typu wyliczeniowego, zauważyłem ku mojemu zdziwieniu, że pole "zaplecza", które zawiera rzeczywistą wartość konkretnej instancji wyliczenia, to nie jest private, jak myślałem, ale public. I to też nie było. (IsPublic prawda, IsInitOnly fałszywe.)Czy typy wyliczeniowe .NET są właściwie zmiennymi typami wartości?

Wielu ludzi uważa „Zmienne” typy wartości w systemie typu NET „zło”, więc dlaczego są typy enum (jako stworzony z kodu C# na przykład) prostu ?

Teraz, jak się okazuje, kompilator C# ma jakąś magię, która zaprzecza istnieniu publicznego pola instancji (ale patrz poniżej), ale np. PowerShell możesz to zrobić:

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

Pole value__ można zapisać na.

Teraz, aby zrobić to w języku C#, musiałem użyć dynamic, ponieważ wydaje się, że przy normalnym powiązaniu z członkiem kompilacji C# udaje, że pole instancji public nie istnieje. Oczywiście do użycia dynamic, będziemy musieli użyć boksowania wartości wyliczeniowej.

Oto przykład kodu 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 

myślę komentarze mówią same za siebie. Możemy zmutować instancję typu wyliczeniowego DayOfWeek (może to być dowolny typ wyliczeniowy z zespołu BCL lub z zespołu "domowego") poprzez pole public. Ponieważ instancja była w zmiennej hashtable, a mutacja prowadzi do zmiany kodu skrótu, instancja znajduje się w niewłaściwym "wiadrze" po mutacji, a funkcja HashSet<> nie może działać.

Dlaczego projektanci .NET zdecydowali się na utworzenie pola instancji typu enum public?

+0

Zastanawiam się nad Waszymi pomysłami, co byłoby idealnym wdrożeniem. Czy chcesz się podzielić? –

+2

W rzeczywistości istnieje znacznie prostszy sposób udowodnienia, że ​​typy enum są zmienne, bez boksu i dynamiczne: https://gist.github.com/thomaslevesque/6100447 –

+0

@KenKin Cóż, przede wszystkim, moim pomysłem jest, że pole instancji która utrzymuje, że rzeczywista wartość bieżącej instancji powinna być "prywatna". To, czy nazwa zawierałaby "zabawne" postacie (postacie niemożliwe w nazwach identyfikatorów w C#), czy nie, byłoby, moim zdaniem, mniej ważne. –

Odpowiedz

20

Pozwólcie, że spróbuję wyjaśnić to mylące pytanie dla czytelników, którzy nie są zaznajomieni z tym, jak za sceną powstają wyliczenia. Kod C#

enum E { A, B } 

się 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) 
} 

też przepisać że C# Ponownie, wyliczenia odpowiada następującym pseudo-C#

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

Zagadnienie jest: dlaczego pole magiczne jest publiczne?

Nie miałem okazji wziąć pod uwagę tej decyzji dotyczącej projektu, więc musiałbym się zgadywać. Moje wykształcone przypuszczenie to: jak zainicjować instancję struktury, jeśli pole nie jest publiczne?

Tworzysz konstruktora, do którego musisz zadzwonić, a to daje pracę jitterowi i jaki jest koszt wydajności tej pracy? Jeśli odpowiedź brzmi: "Kupuje mi środowisko uruchomieniowe, które nie pozwala mi zrobić czegoś głupiego i niebezpiecznego, czego nie powinienem robić w pierwszej kolejności i musiałbym naprawdę ciężko pracować", to podaję ci, że to nie jest przekonujący stosunek kosztów do korzyści.

Ponieważ wystąpienie było w hashtable i prowadzą do mutacji zmiany kodu skrótu, instancja jest w złym „wiadra” po mutacji, a HashSet nie może funkcjonować.

To kilka mil przeszłości „if to boli, kiedy to zrobić wtedy przestać to robić” linii.

+0

Nie wiem wystarczająco dużo na ten temat, aby powiedzieć, czy wystąpiłby koszt wydajności posiadania konstruktora z jednym linkiem do wykonania pracy, ale myślę, że masz rację, wyjaśnienie dotyczy wydajności. Projekt jest pomyślny, jak sądzę, z powodu tej magii w kompilatorze C#, który sprawia, że ​​nie "widzi" publicznego pola. Jeśli "wartość__" pojawiłaby się w intellisense i mogła zostać zapisana (bez "ciężkiej pracy" za pomocą 'dynamic'), wiele osób zapisywałoby się na to pole, co zmieniałoby" praktyczną "semantykę enumów . –

+0

I to jest trochę dziwne, że wiązania w czasie kompilacji z typami niedynamicznymi pomijają pole 'value__', podczas gdy powiązanie w czasie wykonywania z' dynamic' wybiera i używa tego pola bez rzucania. –

+0

Ten ostatni fragment uderza mnie jako błąd. –

Powiązane problemy