2013-05-17 11 views
37

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 der case 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?

+1

Es könnte auch interessant sein zu hören, wenn diese das gleiche mit Mono ist. –

+0

Ich denke, das ist besser geeignet für [Programmierer] (http://programmers.stackexchange.com). – Renan

+5

Während der Compiler nicht betrügt, gibt es eine gute Chance, dass die CLR * * ist. – dlev

Antwort

3

Ich habe keine Antwort, nur einen Hinweis, vielleicht.

Der einzige Unterschied, den ich zwischen String::Empty und System.Diagnostics.Debugger::DefaultCategory sehe, ist die erste, die mit __DynamicallyInvokableAttribute markiert ist.

Ich weiß nicht die Bedeutung dieses undokumentierten Attributs. Eine Frage zu diesem Attribut wurde auf SO gestellt: What is the __DynamicallyInvokable attribute for?

Ich kann nur annehmen, dass dieses Attribut von der Laufzeit für einige Caching gefangen ist?

20

Der Unterschied liegt in der JIT für die neue Version von .NET, die offenbar Verweise auf String.Empty optimiert durch einen Verweis auf eine bestimmte Instanz String inlining anstatt den Wert im Empty Feld gespeichert laden. Dies wird im Rahmen der Definition des init-einzige Einschränkung gerechtfertigt in ECMA-335 Partition I §8.6.1.2, die interpretiert werden kann, um den Wert des String.Empty Feld bedeuten wird sich nicht ändern, nachdem die String Klasse initialisiert wird.

+0

In .NET 4 hatte die 'System.String'-Klasse einen statischen Konstruktor (Methode' .cctor() '), der dem Feld zugewiesen wurde. In .NET 4.5 und höher ist das nicht mehr der Fall. Ich frage mich, warum eine durchgelesene Reflexion (die nicht zu "schummeln" scheint) das Feld nicht als "null" betrachtet. –

2

Weil es kann.

Der Wert dieser systemdefinierten Felder initonly sind globale Invarianten für die .NET-Laufzeit. Wenn diese Invarianten gebrochen sind, gibt es keine irgendwelche Garantien über das Verhalten.

In C++ hätten wir wahrscheinlich eine Regel, die dies als undefiniertes Verhalten bezeichnet. In .NET ist es auch undefiniertes Verhalten, einfach durch das Fehlen einer Regel, die sagt, was passiert, wenn System.String.Empty.Length > 0. Die gesamte Spezifikation aller Schichten von .NET und C# beschreibt das Verhalten, wenn System.String.Empty.Length == 0 und eine ganze Reihe von Invarianten auch gelten.

Weitere Informationen über Optimierungen, die zwischen Laufzeiten und den Auswirkungen variieren, finden die Antworten auf