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:
switch
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 istdas 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?