Kurzversion:Geändertes Verhalten von string.Empty (oder System.String :: Leer) in .NET 4.5
Der C# -Code
typeof(string).GetField("Empty").SetValue(null, "Hello world!");
Console.WriteLine(string.Empty);
wenn kompiliert und ausgeführt, gibt Ausgang "Hello world!"
unter .NET Version 4.0 und früher, aber gibt ""
unter .NET 4.5 und .NET 4.5.1.
Wie kann ein Schreiben in ein Feld ignoriert werden oder wer setzt dieses Feld zurück?
Längere Version:
Ich habe nie wirklich verstanden, warum das string.Empty
Feld (auch als [mscorlib]System.String::Empty
bekannt) nicht const
ist (aka literal
.) Finden Sie unter "Why isn't String.Empty a constant?". Dies bedeutet, dass zum Beispiel in C# wir nicht string.Empty
in den folgenden Situationen verwenden:
- In einer
switch
Anweisung in dercase string.Empty:
- als Standardwert eines optionalen Parameters Form, wie
void M(string x = string.Empty) { }
- Bei der Anwendung ein Attribut, wie
[SomeAttribute(string.Empty)]
- Andere Situationen, in denen eine Kompilierung Zeitkonstante erforderlich ist
, die Auswirkungen auf den bekannten "Religionskrieg" hat, ob oder ""
zu verwenden ist, siehe "In C#, should I use string.Empty or String.Empty or "" to intitialize a string?".
Vor ein paar Jahren habe ich mich amüsiert, indem ich durch Reflektion Empty
auf eine andere String-Instanz gesetzt habe und sehe, wie viele Teile der BCL sich merkwürdig verhalten haben. Es waren ziemlich viele. Und die Änderung der Empty
Referenz schien für die gesamte Lebensdauer der Anwendung zu bestehen. Nun, neulich habe ich versucht, diesen kleinen Trick zu wiederholen, aber dann eine .NET 4.5-Maschine zu verwenden, und ich konnte es nicht mehr tun.
(NB! Wenn Sie .NET 4.5 auf Ihrem Rechner haben, wahrscheinlich Ihre PowerShell
noch eine ältere Version von .NET, so versuchen Sie kopieren-Einfügen [String].GetField("Empty").SetValue($null, "Hello world!")
in Powershell einige Effekte der Änderung dieser Referenz zu sehen.)
Als ich nach einem Grund suchte, bin ich auf den interessanten Thread "What's the cause of this FatalExecutionEngineError in .NET 4.5 beta?" gestoßen. In der akzeptierten Antwort auf diese Frage, ist es notiert, dass durch Version 4.0, System.String
hatte einen statischen Konstruktor .cctor
, in dem das Feld Empty
festgelegt wurde (in der C# Quelle, die wahrscheinlich nur ein Feld Initialisierer natürlich wäre), während 4.5 Es existiert kein statischer Konstruktor. In beiden Versionen sieht das Feld selbst gleich aus:
.field public static initonly string Empty
(wie mit IL DASM gesehen).
Keine anderen Felder als String::Empty
scheint betroffen zu sein. Als ein Beispiel habe ich mit System.Diagnostics.Debugger::DefaultCategory
experimentiert. Dieser Fall scheint analog zu sein: Eine versiegelte Klasse, die ein static readonly
(static initonly
) Feld vom Typ string
enthält. Aber in diesem Fall funktioniert es gut, den Wert (Referenz) durch Reflexion zu ändern.
Zurück zur Frage:
Wie ist es möglich, technisch gesehen, dass Empty
nicht zu ändern scheint (in 4.5), wenn ich das Feld gesetzt? Ich vergewissert haben, dass der C# -Compiler nicht „betrügen“ mit dem Lese, es gibt IL wie:
ldsfld string [mscorlib]System.String::Empty
so dass die tatsächliche Feld sollte gelesen werden.
bearbeiten nach Bounty wurde auf meine Frage gestellt: Beachten Sie, dass der Schreibvorgang (die Reflexion sicher braucht, da das Feld readonly
(auch bekannt als initonly
im IL)) tatsächlich wie erwartet funktioniert. Es ist die lesen Operation, die anomal ist. Wenn Sie mit Reflexion lesen, wie in typeof(string).GetField("Empty").GetValue(null)
, ist alles normal (d. H. Die Änderung des Wertes wird gesehen). Siehe Kommentare unten.
Die bessere Frage ist also: Warum betrügt diese neue Version des Frameworks, wenn es dieses bestimmte Feld liest?
Es könnte auch interessant sein zu hören, wenn diese das gleiche mit Mono ist. –
Ich denke, das ist besser geeignet für [Programmierer] (http://programmers.stackexchange.com). – Renan
Während der Compiler nicht betrügt, gibt es eine gute Chance, dass die CLR * * ist. – dlev