22

While I understand that changing the value of String.Empty would be a bad idea, I don't understand why I can't do it.

To get what I mean, consider the following class:

public class SomeContext 
{ 
    static SomeContext(){}
    public static readonly string Green = "Green";
    public static readonly SomeContext Instance = new SomeContext();

    private SomeContext(){}
    public readonly string Blue = "Blue";

    public static void PrintValues()
    { 
        Console.WriteLine(new { Green, Instance.Blue, String.Empty }.ToString());
    }
}

I have a little console app that tries to manipulate these three readonly fields. It can successfully make Blue and Green into Pink, but Empty stays the same:

        SomeContext.PrintValues();
        ///  prints out : { Green = Green, Blue = Blue, Empty = }
        typeof(SomeContext).GetField("Blue").SetValue(SomeContext.Instance, "Pink");
        typeof(SomeContext).GetField("Green", BindingFlags.Public | BindingFlags.Static).SetValue(null, "Pink");
        typeof(String).GetField("Empty", BindingFlags.Public | BindingFlags.Static).SetValue(null, "Pink");
        SomeContext.PrintValues();
        ///  prints out : { Green = Pink, Blue = Pink, Empty = }

Why?

Originally, I also asked why String.Empty wasn't a constant. I found the answer to this part of my question on another post and deleted that part of the question).

Note: None of the "duplicates" have a conclusive answer to this problem, that's why I'm asking this question.

Community
  • 1
  • 1
smartcaveman
  • 41,281
  • 29
  • 127
  • 212
  • Your comment says Green = Green in the printout... What does it actually say in an actual printout, particularly if you're sure to look for two instances of Pink or not. –  Nov 13 '13 at 21:07
  • This may partly answer your question: http://stackoverflow.com/questions/507923/why-isnt-string-empty-a-constant?rq=1 – artur grzesiak Nov 13 '13 at 21:11
  • 2
    This might answer the question: http://stackoverflow.com/questions/16618302/changed-behavior-of-string-empty-or-system-stringempty-in-net-4-5 –  Nov 13 '13 at 21:14
  • Good find @Amy, I believe that's exactly what we're talking about here! – Mike Perrenoud Nov 13 '13 at 21:16
  • @Amy - that is helpful and definitely provides a lot of background information. I'm still not totally sure why, because there isn't really an answer on that post. I'll follow the links that are cited there see if it leads me to an actual answer – smartcaveman Nov 13 '13 at 21:18
  • Also a duplicate of this older question http://stackoverflow.com/q/6293924/103167 and especially the comments to this answer http://stackoverflow.com/a/6297265/103167 – Ben Voigt Nov 13 '13 at 21:18
  • Let's see if *Deus ex machina* (also known as @Eric Lippert) will show up this time. – Nemanja Boric Nov 13 '13 at 21:21
  • There is definitely similarity to some of the questions that were cited, but since none of the other questions have an answer I'd appreciate if you guys will leave this open long enough that someone who does have knowledge has a chance to answer it. – smartcaveman Nov 13 '13 at 21:22
  • @BenVoigt - Saw your comment about the JIT optimization. That was something I suspected, but do you know this is the case for certain? – smartcaveman Nov 13 '13 at 21:23
  • @smartcaveman: I know it's legal for the JIT to do it. I know I've seen some JIT versions (in Mono) that do, in some cases. To know whether your JIT will do it in your case, you should disassemble the machine code which the JIT generated. – Ben Voigt Nov 13 '13 at 21:25
  • 4
    Short answer: You are changing `System.String.Empty`. The problem is that your code to inspect the current value of `System.String.Empty` has broken, because you've violated its invariants. Start violating system invariants and nothing can be relied on. – Ben Voigt Nov 13 '13 at 21:28
  • @BenVoigt - I've reflected on the source and I don't see any place where `MdFieldInfo.GetValue(...)` or `RtFieldInfo.GetValue(...)` have a dependency on `String.Empty`. Do you have a specific place in mind that I'm missing? – smartcaveman Nov 13 '13 at 21:32
  • 1
    I've noticed that `Getvalue` returns "Pink" though Accessing field returns "" – Sriram Sakthivel Nov 13 '13 at 21:34
  • @BenVoigt - I've never gone any lower than IL. Can you point me in a direction of where I can learn how to disassemble and interpret the machine code so that I could check this out myself? – smartcaveman Nov 13 '13 at 21:35
  • @SriramSakthivel - +1 , that's interesting. It also seems like it would make sense with what BenVoigt is saying about the JIT optimization – smartcaveman Nov 13 '13 at 21:37
  • 1
    See [How do I draw attention to old unanswered questions](http://meta.stackexchange.com/questions/7046/how-do-i-get-attention-for-old-unanswered-questions). If there is a question you've found that isn't answered the solution is not to ask it again. Consider putting a bounty on the duplicate post instead. – Servy Nov 13 '13 at 21:37
  • @Servy - The problem is I don't feel that any of the older questions make the question as clear. Just read their titles. – smartcaveman Nov 13 '13 at 21:37
  • Please refrain from large text just for the sake of making your point stand out. It's not welcome. Meta commentary should generally be kept in the comments or when there's a lot of it, moved to meta. It should be kept out of the question itself. – George Stocker Nov 13 '13 at 21:39
  • @GeorgeStocker - is that a fair compromise? – smartcaveman Nov 13 '13 at 21:40
  • @smartcaveman IL doesn't lie, but the CLR does. – Sriram Sakthivel Nov 13 '13 at 21:41
  • @smartcaveman Then edit them to improve their titles, to better clarify the question that they're asking. – Servy Nov 13 '13 at 21:52
  • Using `FieldInfo.GetValue` returns the set value of `String.Empty`, even though `String.Empty` itself does not. Is the CLR caching the value in memory? –  Nov 13 '13 at 21:55
  • 1
    @Servy - the intentions in their questions are different. All three discuss the same basic line of code. But, there are very different reasons in each question. If I changed their questions to look like mine, I'd be misrepresenting their questions. Check them out and tell me if you think differently. – smartcaveman Nov 13 '13 at 21:57
  • @Amy - no real way to know. It comes down to `RuntimeFieldHandle.GetValue(...)`, which is `extern`. I didn't see any caching on the way there though – smartcaveman Nov 13 '13 at 21:59
  • @smartcaveman The duplicate is asking why the behavior changed in .NET 4.5. You're asking why it behaves that way (not realizing it's specific to 4.5). Answering that question would answer yours entirely. You don't *need* change their question to be your question. If you feel their question's titles don't represent the question they are asking effectively, you can edit them to improve them. – Servy Nov 13 '13 at 22:01
  • @Servy , that's definitely the closest. But then there's the problem of that question having a selected answer which doesn't answer my question at all. There is no doubt that they are related, but if the answer on that post is sufficient for that OP's question, then there's no way we are getting at the same thing. – smartcaveman Nov 13 '13 at 22:06
  • @smartcaveman I then refer you back to my earlier comment. If you want a better answer to a question than what's there, post a bounty, rather than a duplicate question. – Servy Nov 13 '13 at 22:07
  • @smartcaveman: I just improved my answer on the oldest question to cover all these related issues. – Ben Voigt Nov 14 '13 at 17:23

1 Answers1

16

You can't change it because you have .NET 4.5 on your machine. Just change the Framework Target setting of your project to 3.5 and you'll see it works.

The CLR has built-in knowledge of String.Empty. You'll for example see with a Reflector or the Reference Source that the System.String class never initializes it. It is done during CLR startup. Exactly how 4.5 prevents the modification from being visible is a bit hard to tell. You actually did modify the field, just add this line of code:

  var s = typeof(String).GetField("Empty", 
             BindingFlags.Public | BindingFlags.Static).GetValue(null);
  Console.WriteLine(s);

And you'll see "Pink". My guess is that it is intercepted by the jitter. I can't give hard evidence for that however. There's precedent, try tinkering with Decimal.MaxValue for example, another readonly static value. Not changeable in 3.5 either, the jitter recognizes it and generates the value directly without reading the field.

I just found out you put a bounty on another question with the exact same subject. 280Z28's post is pretty similar.

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • +1 - thank you for this - Can you suggest an approach for finding hard evidence about what JIT optimizations are taking place? This was discussed some in the comments, and what I got is that I would need to disassemble the JIT generated machine code. Unfortunately, I don't know how to access anything at a lower level than IL. I'm not asking for an in depth explanation, but a nudge in the right direction would be very much appreciated. – smartcaveman Nov 14 '13 at 15:54
  • 2
    It is utterly unclear to me why this problem needs to be solved. Disassembly doesn't get you anywhere, you'll see the raw address being used that the jitter generated. Which doesn't tell you anything more that what you already know, it points to an empty string. You will need v4.5 jitter source code, you cannot get it unless you apply for a job at Microsoft. – Hans Passant Nov 14 '13 at 16:04
  • Hans, you're clearly a very smart guy with a deep and pervasive understanding of software. Did you acquire that understanding by only ever looking into issues that had an immediate practical benefit? Or, would you say that you have on occasion experienced some genuine passion for figuring out how things work simply for the sake of learning how things work and that indulging this drive has been a contributing factor in attaining your level of expertise? – smartcaveman Nov 19 '13 at 09:22