Version courte:
Le code C#
typeof(string).GetField("Empty").SetValue(null, "Hello world!");
Console.WriteLine(string.Empty);
lors de la compilation et de l'exécution, donne de sortie "Hello world!"
sous .NET version 4.0 et versions antérieures, mais qui donne des ""
sous .NET 4.5 et .NET 4.5.1.
Comment écrire à un champ soit ignoré comme ça, ou qui réinitialise ce domaine?
Version longue:
Je n'ai jamais vraiment compris pourquoi l' string.Empty
champ (également connu en tant que [mscorlib]System.String::Empty
) n'est pas const
(aka. literal
), voir "Pourquoi n'est-il pas de Chaîne.Vide une constante?". Cela signifie que, par exemple, en C#, nous ne pouvons pas utiliser string.Empty
dans les situations suivantes:
- En
switch
déclaration dans la formecase string.Empty:
- Comme la valeur par défaut d'un paramètre facultatif, comme
void M(string x = string.Empty) { }
- Lors de l'application d'un attribut, comme
[SomeAttribute(string.Empty)]
- D'autres situations où une constante de compilation est nécessaire
qui a des implications pour le bien-connu "guerre de religion" sur la question de l'utilisation string.Empty
ou ""
, voir "En C#, dois-je utiliser des chaînes de caractères.Vide ou Chaîne.Vide ou "de"?".
Il y A quelques années je m'amusais à en définissant Empty
d'une autre chaîne instance par la réflexion, et de voir comment de nombreuses parties de la BCL a commencé à se comporter bizarrement à cause de cela. C'était assez nombreux. Et le changement de l' Empty
référence semble persister pendant la durée complète de l'application. Maintenant, l'autre jour, j'ai essayé de répéter que peu de stunt, mais ensuite, à l'aide d'un .NET 4.5 machine, et je ne pouvais pas faire plus.
(NB! Si vous en avez .NET 4.5 sur votre machine, probablement votre PowerShell
encore utilise une ancienne version de .NET, alors essayez de copier-coller [String].GetField("Empty").SetValue($null, "Hello world!")
en PowerShell pour voir quelques-uns des effets de la modification de cette référence.)
Quand j'ai essayé de chercher une raison à cela, je suis tombé sur le fil intéressant "Quelle est la cause de cette FatalExecutionEngineError dans .NET 4.5 beta?". Dans la accepté de répondre à cette question, est-il noté que jusqu'à la version 4.0, System.String
avaient un constructeur statique .cctor
dans lequel le champ Empty
a été fixé (dans le source C#, qui serait probablement juste être un champ d'initialiseur, bien sûr), tout en 4,5 aucun constructeur statique existe. Dans les deux versions, le champ lui-même semble le même:
.field public static initonly string Empty
(comme on le voit avec l'IL DASM).
Pas d'autres domaines que la String::Empty
semble être affecté. Comme un exemple, j'ai testé avec System.Diagnostics.Debugger::DefaultCategory
. Ce cas semble analogue: Une classe scellée contenant un static readonly
(static initonly
) champ de type string
. Mais dans ce cas, il fonctionne très bien pour modifier la valeur (de référence) par le biais de la réflexion.
Retour à la question:
Comment est-il possible, techniquement, c' Empty
ne semble pas changer (4,5) quand j'ai mis le champ? J'ai vérifié que le compilateur C# ne pas "tricher" avec la lecture, c'sorties IL aime:
ldsfld string [mscorlib]System.String::Empty
si le champ doit être lu.
Edit après bounty a été mis sur ma question: Notez que l'opération d'écriture (qui a besoin de la réflexion, pour sûr, car le champ est - readonly
(un.k.un. initonly
dans l'IL)) en fait, il fonctionne comme prévu. C'est la lecture d'une utilisation anormale. Si vous lisez avec la réflexion, en typeof(string).GetField("Empty").GetValue(null)
,, tout est normal (c'est à dire le changement de valeur est vu). Voir les commentaires ci-dessous.
Donc la question est: Pourquoi cette nouvelle version du cadre de tricher lorsqu'il lit ce domaine particulier?