35

I'm trying to understand why string.Empty is readonly and not a const. I saw this Post but I don't understand the comment Microsoft wrote about it. As Jon Skeet wrote in a comment "I don't know - it doesn't make much sense to me, to be honest..."

Shared Source Common Language Infrastructure 2.0 Release. string.cs is in sscli20\clr\src\bcl\system\string.cs

// The Empty constant holds the empty string value.
//We need to call the String constructor so that the compiler doesn't mark this as a literal.
//Marking this as a literal would mean that it doesn't show up as a field which we can access 
//from native.
public static readonly String Empty = ""; 

I can't see here any String constructor call and furthermore, it's marked as literal - ""

Can someone please explain me in plain text, What does the comment mean and why is string.Empty readonly and not a const?


Update:
Eric Lippert commented on by now a deleted answer:

I asked one of the C# old-timers over lunch about this and he did not recall specifically why this decision was made, but conjectured that it had something to do with interning.

Community
  • 1
  • 1
gdoron
  • 147,333
  • 58
  • 291
  • 367
  • 9
    Maybe it's one of those comments that made sense at one point, but then the code was changed and it no longer makes sense... not that I've ever done that or anything... *whistles as he walks away* –  Dec 13 '11 at 07:14
  • 49
    "Why isn't it a const" - because then you couldn't have fun doing `typeof(string).GetField("Empty").SetValue(null, null);` or `typeof(string).GetField("Empty").SetValue(null, " ");` - mwahahah; mwaaaahahahahahahhh; MWWWWAAAAAHAHAHAHAHAHH! – Marc Gravell Dec 13 '11 at 07:24
  • 8
    Based on the comment, it appears that consts were not considered fields at some point in the past. – Raymond Chen Dec 13 '11 at 14:35

2 Answers2

16

The important part is not what happens IN this class, but what happens, when another class uses (and links to) it. Let me explain with another example:

Assume you have a Assembly1.dll containing a class declaring

public static const int SOME_ERROR_CODE=0x10;
public static readonly int SOME_OTHER_ERROR_CODE=0x20;

and another class consuming this e.g.

public int TryFoo() {
    try {foo();}
    catch (InvalidParameterException) {return SOME_ERROR_CODE;}
    catch (Exception) { return SOME_OTHER_ERROR_CODE;}
    return 0x00;
}

You compile your class into Assembly2.dll and link it against Assembly1.dll, as expected, your method will return 0x10 on invalid parameters, 0x20 on other errors, 0x00 on success.

Especially, if you create Assembly3.exe containing something like

int errorcode=TryFoo();
if (errorcode==SOME_ERROR_CODE) bar();
else if (errorcode==SOME_OTHER_ERROR_CODE) baz();

It will work as expected (After being linked against Assembly1.dll and Assembly2.dll)

Now if you get a new version of Assembly1.dll, that has

public const int SOME_ERROR_CODE=0x11;
public readonly int SOME_OTHER_ERROR_CODE=0x21;

If you recompile Assembly3.exe and link the last fragment against new Assembly1.dll and unchanged Assembly2.dll, it will stop working as expected:

bar() will NOT be called correctly: Assembly2.dll remembers the LITERAL 0x20, which is not the same literal 0x21 that Assembly3.exe reads out of Assembly1.dll

baz() will be called correctly: Both Assembly2.dll and Assembly3.exe refer to the SYMBOL REFERENCE called SOME_OTHER_ERROR_CODE, which is in both cases resolved by the current version of Assembly1.dll, thus in both cases is 0x21.

In Short: a const creates a LITERAL, a readonly creates a SYMBOL REFERENCE.

LITERALS are internal to the framework and can not be marshalled and thus used by native code.

So

public static readonly String Empty = ""; 

creates a symbol reference (resovled at time of first use by a call to the String cosntuctor), that can be marshalled an thus used from native, while

public static const String Empty = ""; 

would create a literal, that can't.

gdoron
  • 147,333
  • 58
  • 291
  • 367
Eugen Rieck
  • 64,175
  • 10
  • 70
  • 92
  • 3
    What are the benefits that string.Empty isn't "remembered" it's not going to be changed. const could be fine, ins't it? – gdoron Dec 19 '11 at 03:00
  • 1
    I suspect it is about marshalling: The code comment talks about being able to call it from native, and literals are not marshallable. – Eugen Rieck Dec 19 '11 at 11:13
  • 2
    I would like to understand why literals are not marshallable... What's different between a literal and a reference that returns the same value? – Camilo Martin Jan 29 '12 at 14:45
  • 3
    Although this answer correctly describes why `readonly` is used, it in no way answers the question. – Mark Hurd Jan 08 '13 at 12:02
  • @MarkHurd The last sentence of the OQ is "why is string.Empty readonly and not a const" - so I thought, if it "correctly describes why readonly is used" it indeed does answer the question. I might be wrong, though! – Eugen Rieck Jan 08 '13 at 12:07
  • @EugenRieck Good point. I saw the title and much of the discussion and commentary, but missed that :-( – Mark Hurd Jan 08 '13 at 12:20
  • @MarkHurd So the OQ consists of 2 parts: The latter we discussed above. The former is "What does the comment mean". The comment is "Do not make this a literal, because we can't access the lieral from native". The last part of my answer (starting with "In Short") talks about literals being unmarshable and thus not being usable outside the framework - IMHO this answers the first part of the OQ – Eugen Rieck Jan 08 '13 at 12:30
0

This doesn't directly answer the why but it gives some additional context. String.Empty is treated as intrinsic by the runtime and the JIT. It works exactly same as "" underneath. Both will give you pointer to the empty string singleton and the JITed code will look same for both as well. It is just a style matter which one you use. Furthermore, there is no difference in memory allocation.

Dave Black
  • 7,305
  • 2
  • 52
  • 41