Versione corta:comportamento Cambiato di string.Empty (o System.String :: Vuoto) in .NET 4.5
Il codice C#
typeof(string).GetField("Empty").SetValue(null, "Hello world!");
Console.WriteLine(string.Empty);
quando compilato ed eseguito, dà uscita "Hello world!"
sotto .NET versione 4.0 e precedenti, ma fornisce ""
in .NET 4.5 e .NET 4.5.1.
In che modo una scrittura su un campo può essere ignorata in questo modo oppure, chi resetta questo campo?
versione più lunga:
non ho mai veramente capito il motivo per cui il campo string.Empty
(noto anche come [mscorlib]System.String::Empty
) non è const
(aka literal
.), Vedere "Why isn't String.Empty a constant?". Questo significa che, per esempio, in C# non possiamo usare string.Empty
nelle seguenti situazioni:
- In una dichiarazione
switch
in formacase string.Empty:
- come valore predefinito di un parametro opzionale, come
void M(string x = string.Empty) { }
- Quando si applica un attributo, come
[SomeAttribute(string.Empty)]
- Altre situazioni in cui è richiesta una costante della fase di compilazione
che ha implicazioni per la ben nota "guerra religiosa" sull'uso di string.Empty
o ""
, vedere "In C#, should I use string.Empty or String.Empty or "" to intitialize a string?".
Un paio di anni fa mi sono divertito impostando Empty
in un'altra istanza di stringa attraverso il riflesso e vedere quante parti del BCL iniziarono a comportarsi in modo strano a causa di esso. Ne erano parecchie. E il cambiamento del riferimento Empty
sembrava persistere per tutta la vita dell'applicazione. Ora, l'altro giorno ho provato a ripetere quella piccola acrobazia, ma poi usando una macchina .NET 4.5, e non potevo più farlo.
(NB! Se si dispone di .NET 4.5 sul computer, probabilmente la PowerShell
utilizza ancora una versione precedente di .NET, quindi cercate copia-incolla [String].GetField("Empty").SetValue($null, "Hello world!")
in PowerShell per vedere alcuni effetti di cambiare questo riferimento.)
Quando ho provato a cercare un motivo, mi sono imbattuto nel thread interessante "What's the cause of this FatalExecutionEngineError in .NET 4.5 beta?". Nella risposta accettata a questa domanda, si nota che tramite la versione 4.0, System.String
aveva un costruttore statico .cctor
in cui era impostato il campo Empty
(nel sorgente C#, probabilmente sarebbe solo un inizializzatore di campo, ovviamente), mentre in 4.5 non esiste un costruttore statico. In entrambe le versioni, il campo stesso ha lo stesso aspetto:
.field public static initonly string Empty
(come visto con IL DASM).
Nessun altro campo oltre String::Empty
sembra essere interessato. Ad esempio, ho sperimentato con System.Diagnostics.Debugger::DefaultCategory
. Questo caso sembra analogo: una classe sigillata contenente un campo static readonly
(static initonly
) di tipo string
. Ma in questo caso funziona bene per cambiare il valore (riferimento) attraverso la riflessione.
Tornando alla domanda:
Come è possibile, tecnicamente, che Empty
non sembra cambiare (in 4.5), quando ho impostato il campo? Ho verificato che il compilatore C# non "barare" con la lettura, emette IL simile:
ldsfld string [mscorlib]System.String::Empty
modo che il campo reale dovrebbe essere letto.
Modifica dopo taglie è stato messo sulla mia domanda: Si noti che l'operazione di scrittura (che ha bisogno di riflessione di sicuro, dal momento che il campo è readonly
(anche noto come initonly
nella IL)) in realtà funziona come previsto. È il leggere operazione che è anomala. Se si legge con la riflessione, come in typeof(string).GetField("Empty").GetValue(null)
, tutto è normale (vale a dire il cambio di valore è visto). Vedi i commenti qui sotto.
Quindi la domanda migliore è: perché questa nuova versione del cheat del framework quando legge questo particolare campo?
Potrebbe anche essere interessante sentire se questo è lo stesso con Mono. –
Penso che questo sia più adatto per [Programmers] (http://programmers.stackexchange.com). – Renan
Mentre il compilatore non sta barando, c'è una buona possibilità che il CLR * sia *. – dlev