Geändertes Verhalten von string.Empty (oder System.String :: Empty) in .NET 4.5

Kurzfassung:

Der C # -Code

typeof(string).GetField("Empty").SetValue(null, "Hello world!");
Console.WriteLine(string.Empty);

Gibt beim Kompilieren und Ausführen eine Ausgabe aus"Hello world!" unter .NET Version 4.0 und früher, gibt aber"" unter .NET 4.5 und .NET 4.5.1.

Wie kann ein Schreiben in ein Feld so ignoriert werden oder wer setzt dieses Feld zurück?

Längere Version:

Ich habe nie wirklich verstanden, warum das so iststring.Empty Feld (auch bekannt als[mscorlib]System.String::Empty) ist nichtconst (aka.literal), sehen "Warum ist String.Empty keine Konstante?Msgstr "" "Dies bedeutet, dass wir zum Beispiel in C # nicht verwenden könnenstring.Empty in folgenden Situationen:

In einemswitch Aussage im Formularcase string.Empty:Als Standardwert eines optionalen Parameters, wie zvoid M(string x = string.Empty) { }Wenn Sie ein Attribut anwenden, z[SomeAttribute(string.Empty)]Andere Situationen, in denen eine Konstante für die Kompilierungszeit erforderlich ist

das hat Auswirkungen auf den bekannten "Religionskrieg" über die Verwendungstring.Empty oder"", sehen "Sollte ich in C # string.Empty oder String.Empty oder "" verwenden, um einen String zu initialisieren?".

Vor ein paar Jahren habe ich mich amüsiertEmpty zu einer anderen String-Instanz durch Reflektion, und sehen Sie, wie viele Teile der BCL sich aufgrund dessen seltsam verhalten haben. Es waren ziemlich viele. Und die Veränderung derEmpty Die Referenz schien für die gesamte Laufzeit der Anwendung bestehen zu bleiben. Nun, neulich habe ich versucht, diesen kleinen Stunt zu wiederholen, aber dann mit einem .NET 4.5-Computer, und ich konnte es nicht mehr tun.

(NB! Wenn Sie .NET 4.5 auf Ihrem Computer haben, ist wahrscheinlich IhrPowerShell Verwendet immer noch eine ältere Version von .NET. Versuchen Sie also das Kopieren und Einfügen[String].GetField("Empty").SetValue($null, "Hello world!") in PowerShell, um einige Auswirkungen der Änderung dieser Referenz zu sehen.)

Als ich nach einem Grund dafür gesucht habe, bin ich auf den interessanten Thread gestoßen "Was ist die Ursache für diesen FatalExecutionEngineError in .NET 4.5 Beta?". Ist in der akzeptierten Antwort auf diese Frage vermerkt, dass durch Version 4.0,System.String hatte einen statischen Konstruktor.cctor in dem das FeldEmpty wurde gesetzt (in der C # -Quelle wäre das wahrscheinlich natürlich nur ein Feldinitialisierer), während in 4.5 kein statischer Konstruktor existiert. In beiden Versionen sieht das Feld selbst gleich aus:

.field public static initonly string Empty

(wie mit IL DASM gesehen).

Keine anderen Felder alsString::Empty scheint betroffen zu sein. Als Beispiel habe ich mit experimentiertSystem.Diagnostics.Debugger::DefaultCategory. Dieser Fall scheint analog zu sein: Eine versiegelte Klasse mit astatic readonly (static initonly) Feld des Typsstring. In diesem Fall funktioniert es jedoch einwandfrei, den Wert (Referenz) durch Reflektion zu ändern.

Zurück zur Frage:

Wie ist es technisch möglich, dassEmpty scheint sich (in 4.5) nicht zu ändern, wenn ich das Feld einstelle? Ich habe überprüft, dass der C # -Compiler nicht mit dem Lesen "schummelt", sondern IL wie folgt ausgibt:

ldsfld     string [mscorlib]System.String::Empty

also sollte das eigentliche Feld gelesen werden.

Bearbeiten, nachdem Kopfgeld auf meine Frage gelegt wurde: Beachten Sie, dass die Schreiboperation (die Reflexion benötigt, da das Feld ist)readonly (a.k.a.initonly in der IL)) funktioniert tatsächlich wie erwartet. Es ist derlesen Operation, die anomal ist. Wenn Sie mit Nachdenken lesen, wie intypeof(string).GetField("Empty").GetValue(null)ist alles normal (d. h. die Wertänderung ist zu sehen). Siehe Kommentare unten.

Die bessere Frage ist also: Warum betrügt diese neue Version des Frameworks, wenn es dieses spezielle Feld liest?

Antworten auf die Frage(3)

Ihre Antwort auf die Frage