Zmieniono zachowanie string.Empty (lub System.String :: Empty) w .NET 4.5
Krótka wersja:
Kod C #
typeof(string).GetField("Empty").SetValue(null, "Hello world!");
Console.WriteLine(string.Empty);
po skompilowaniu i uruchomieniu daje dane wyjściowe"Hello world!"
w wersji .NET 4.0 i wcześniejszych, ale daje""
w .NET 4.5 i .NET 4.5.1.
W jaki sposób można w ten sposób zignorować zapis do pola lub kto resetuje to pole?
Dłuższa wersja:
Nigdy tak naprawdę nie rozumiałem, dlaczegostring.Empty
pole (znane również jako[mscorlib]System.String::Empty
) nie jestconst
(znany jako.literal
), widzieć "Dlaczego String.Empty nie jest stałą?". Oznacza to, że na przykład w C # nie możemy użyćstring.Empty
w następujących sytuacjach:
switch
oświadczenie w formularzucase string.Empty:
Jako domyślna wartość opcjonalnego parametru, jakvoid M(string x = string.Empty) { }
Przy stosowaniu atrybutu, jak[SomeAttribute(string.Empty)]
Inne sytuacje, w których wymagana jest stała czasu kompilacjico ma wpływ na dobrze znaną „wojnę religijną” nad tym, czy używaćstring.Empty
lub""
, widzieć "Czy w C # powinienem użyć string.Empty lub String.Empty lub „”, aby zainicjować ciąg?„
Kilka lat temu bawiłem się, ustawiającEmpty
do jakiejś innej instancji ciągu poprzez refleksję i zobacz, jak wiele części BCL zaczęło dziwnie zachowywać się z tego powodu. Było dość dużo. I zmianaEmpty
odniesienie wydawało się utrzymywać przez cały okres użytkowania aplikacji. Teraz, pewnego dnia, próbowałem powtórzyć ten mały wyczyn, ale potem użyłem komputera .NET 4.5 i nie mogłem już tego zrobić.
(Uwaga! Jeśli masz na komputerze .NET 4.5, prawdopodobnie twójPowerShell
nadal używa starszej wersji .NET, więc spróbuj wkleić kopię[String].GetField("Empty").SetValue($null, "Hello world!")
do PowerShell, aby zobaczyć efekty zmiany tego odwołania.)
Kiedy próbowałem znaleźć przyczynę, natknąłem się na interesujący wątek ”Jaka jest przyczyna tego FatalExecutionEngineError w .NET 4.5 beta?„Czy w przyjętej odpowiedzi na to pytanie zauważono, że w wersji 4.0,System.String
miał konstruktora statycznego.cctor
w którym poleEmpty
został ustawiony (w źródle C #, prawdopodobnie byłby to oczywiście inicjator pola), podczas gdy w 4.5 nie istnieje żaden konstruktor statyczny. W obu wersjach samo pole wygląda tak samo:
.field public static initonly string Empty
(jak widać z IL DASM).
Brak innych pól niżString::Empty
wydaje się być dotknięty. Jako przykład eksperymentowałem zSystem.Diagnostics.Debugger::DefaultCategory
. Ten przypadek wydaje się analogiczny: klasa zapieczętowana zawierająca astatic readonly
(static initonly
) pole typustring
. Ale w tym przypadku dobrze działa zmiana wartości (odniesienia) poprzez odbicie.
Powrót do pytania:
Jak to jest technicznie możliweEmpty
nie wydaje się zmieniać (w 4.5), kiedy ustawiam pole? Sprawdziłem, że kompilator C # nie „oszukuje” z odczytem, wyprowadza IL jak:
ldsfld string [mscorlib]System.String::Empty
więc rzeczywiste pole powinno być odczytane.
Edycja po nagród została umieszczona na moim pytaniu: Zauważ, że operacja zapisu (która wymaga pewności, ponieważ pole jestreadonly
(znany jako.initonly
w IL)) faktycznie działa zgodnie z oczekiwaniami. To jestczytać operacja, która jest anomalna. Jeśli czytasz z refleksją, jak wtypeof(string).GetField("Empty").GetValue(null)
, wszystko jest normalne (tj. widać zmianę wartości). Zobacz komentarze poniżej.
Lepsze pytanie brzmi więc: dlaczego ta nowa wersja frameworka oszukuje, gdy czyta to konkretne pole?