Reputation: 12674
According to this answer, ""
and string.Empty
are very slightly different, in that ""
creates an object, whereas string.Empty
does not. That answer has the most votes on that question.
However, this answer says that there is no difference. It's a more recent answer as well.
So this was in 2010. Is this really the case? Do string.Empty
and ""
differ at all, even slightly?
EDIT: This question is meant to be an update to the linked questions, as I found it confusing that no modern answer had been presented, despite some debate on the matter.
Upvotes: 6
Views: 1271
Reputation: 273179
The language specification (C# 4.0) is actually silent on the subject.
According to CLR via C#, it depends entirely on the CLR and not on the C# compiler. A relevant quote from p. 341:
Even if an assembly has this attribute/flag [CompilationRelaxations.NoStringInterning] specified, the CLR may choose to intern the strings, but you should not count on this. In fact, you really should never write code that relies on strings being interned unless you have written code that explicitly calls the String’s Intern method yourself.
So using ""
may or may not create an new string object. That depends on the CLR (version) being used. And there's also the possibility that the compiler folds constants, in which case ""
would cost 1 object per assembly compilation unit, not per occurrence.
None of this has any relevant impact on memory use or speed, but the clear guideline should be that both ReferenceEquals(s1, "")
and ReferenceEquals(s1, String.Empty)
should be avoided.
And of course Object.Equals(s1, s2)
and s1 == s2
always work fine on strings.
Upvotes: 7
Reputation: 192
Since some people already commented about what the CLR does/doesn't and object.RefereceEquals, I will provide two more input data.
Performance wise, there is no significant difference whatsoever. I used this code to test it:
Sample Code:
public class StringsTest
{
private int i, maxValue = 500000000;
string myString = "test";
public long CompareToEmptyQuotes()
{
Stopwatch sw = Stopwatch.StartNew();
sw.Start();
for (i = 0; i < maxValue; i++)
{
if (myString == "")
{
;
}
}
sw.Stop();
Debug.WriteLine("Elpased Time: {0}", sw.ElapsedMilliseconds);
return sw.ElapsedMilliseconds;
}
public long CompareToEmptyString()
{
Stopwatch sw = Stopwatch.StartNew();
sw.Start();
for (i = 0; i < maxValue; i++)
{
if (myString == string.Empty)
{
;
}
}
sw.Stop();
Debug.WriteLine("Elpased Time: {0}", sw.ElapsedMilliseconds);
return sw.ElapsedMilliseconds;
}
}
With regard to the generated code, this is how the MSIL looks like:
Use of "" generates: IL_001e: ldstr ""
Use of string.Empty generates: IL_001e: ldsfld string [mscorlib]System.String::Empty
This question here expands on the difference (or equality) between ldstr and ldsfld.
Upvotes: -1
Reputation: 2440
You can read right in String.Empty, the value of this field (String.Empty
) is the zero-length string, ""
.
You can test that they refer to the same string with a small test.
void main()
{
String first = "";
String second = String.empty;
bool test = Object.ReferenceEquals(first, second);
}
Test will return true which means they refer to the same object.
Another slight difference is that evaluating ""
is slightly more optimal and efficient than String.empty
.
EDIT:
static void Main ( string[] args )
{
string s1 = "MyTest";
string s2 = new StringBuilder().Append("My").Append("Test").ToString();
string s3 = String.Intern(s2);
Console.WriteLine((Object)s2 == (Object)s1); // Different references.
Console.WriteLine((Object)s3 == (Object)s1); // The same reference.
Console.WriteLine();
s1 = "";
s2 = new StringBuilder().Append("").ToString();
s3 = String.Empty;
string s4 = String.Intern(s2);
Console.WriteLine((Object)s2 == (Object)s1); // The same reference.
Console.WriteLine((Object)s3 == (Object)s1); // The same reference.
Console.WriteLine((Object)s4 == (Object)s1); // The same reference.
Console.WriteLine((Object)s4 == (Object)s2); // The same reference.
}
It is quite obvious to tell with interning that a string literal is already interned. When stringbuilder
creates a new String object, that has the same object, it isn't interned and the references to the object are not the same. However, when we String.Intern(s2)
the method returns the same reference that is assigned to s1
and the reference is then assigned to s3
.
Thought
Why then, do all the four cases return true. I know the string literal ""
is interned, but why isn't using Stringbuilder
to append ""
to a new string object the same as ""
even though technically it is a new object that is not interned.
Upvotes: 0