Note that there are some explanatory texts on larger screens.

plurals
  1. POChanged behavior of string.Empty (or System.String::Empty) in .NET 4.5
    primarykey
    data
    text
    <p><strong>Short version:</strong></p> <p>The C# code</p> <pre><code>typeof(string).GetField("Empty").SetValue(null, "Hello world!"); Console.WriteLine(string.Empty); </code></pre> <p>when compiled and run, gives output <code>"Hello world!"</code> under .NET version 4.0 and earlier, but gives <code>""</code> under .NET 4.5 and .NET 4.5.1.</p> <p>How can a write to a field be ignored like that, or, who resets this field?</p> <p><strong>Longer version:</strong></p> <p>I have never really understood why the <code>string.Empty</code> field (also known as <code>[mscorlib]System.String::Empty</code>) is not <code>const</code> (aka. <code>literal</code>), see "<a href="https://stackoverflow.com/questions/507923/">Why isn&#39;t String.Empty a constant?</a>". This means that, for example, in C# we can't use <code>string.Empty</code> in the following situations:</p> <ul> <li>In a <code>switch</code> statement in the form <code>case string.Empty:</code></li> <li>As the default value of an optional parameter, like <code>void M(string x = string.Empty) { }</code></li> <li>When applying an attribute, like <code>[SomeAttribute(string.Empty)]</code></li> <li>Other situations where a compile-time constant is required</li> </ul> <p>which has implications to the well-known "religious war" over whether to use <code>string.Empty</code> or <code>""</code>, see "<a href="https://stackoverflow.com/questions/263191/">In C#, should I use string.Empty or String.Empty or &quot;&quot; to intitialize a string?</a>".</p> <p>A couple of years ago I amused myself by setting <code>Empty</code> to some other string instance through reflection, and see how many parts of the BCL started behaving strangely because of it. It was quite many. And the change of the <code>Empty</code> reference seemed to persist for the complete life of the application. Now, the other day I tried to repeat that little stunt, but then using a .NET 4.5 machine, and I couldn't do it anymore.</p> <p>(NB! If you have .NET 4.5 on your machine, probably your <code>PowerShell</code> still uses an older version of .NET, so try copy-pasting <code>[String].GetField("Empty").SetValue($null, "Hello world!")</code> into PowerShell to see some effects of changing this reference.)</p> <p>When I tried to search for a reason for this, I stumbled upon the interesting thread "<a href="https://stackoverflow.com/questions/11873798/">What&#39;s the cause of this FatalExecutionEngineError in .NET 4.5 beta?</a>". In the accepted answer to that question, is it noted that through version 4.0, <code>System.String</code> had a static constructor <code>.cctor</code> in which the field <code>Empty</code> was set (in the C# source, that would probably just be a field initializer, of course), while in 4.5 no static constructor exists. In both versions, the field itself looks the same:</p> <pre><code>.field public static initonly string Empty </code></pre> <p>(as seen with IL DASM).</p> <p>No other fields than <code>String::Empty</code> seems to be affected. As an example, I experimented with <code>System.Diagnostics.Debugger::DefaultCategory</code>. This case seems analogous: A sealed class containing a <code>static readonly</code> (<code>static initonly</code>) field of type <code>string</code>. But in this case it works fine to change the value (reference) through reflection.</p> <p><strong>Back to the question:</strong></p> <p>How is it possible, technically, that <code>Empty</code> doesn't seem to change (in 4.5) when I set the field? I have verified that the C# compiler does not "cheat" with the read, it outputs IL like:</p> <pre><code>ldsfld string [mscorlib]System.String::Empty </code></pre> <p>so the actual field ought to be read.</p> <hr> <p><strong>Edit after bounty was put on my question:</strong> Note that the write operation (which needs reflection for sure, since the field is <code>readonly</code> (a.k.a. <code>initonly</code> in the IL)) actually works as expected. It is the <strong><em>read</em></strong> operation which is anomalous. If you read with reflection, as in <code>typeof(string).GetField("Empty").GetValue(null)</code>, everything is normal (i.e. the change of value is seen). See comments below.</p> <p>So the better question is: Why does this new version of the framework cheat when it reads this particular field?</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload