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:

Wswitch 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 kompilacji

co 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?

questionAnswers(3)

yourAnswerToTheQuestion