G
G.Doten
Peter said:G.Doten said:Well, that just says it's documented - it doesn't explain *how* it
happens. Calling a constructor should logically (and even according
to the C# spec) create a new object. You certainly can't create a C#
class which has this behaviour, for instance.
Sure you can. All the char[] constructor is doing is looking to see if
the passed-in char array is null or if it has 0 elements, in which
case it sets it's internal string storage to the string.Empty
instance. Any class can easily be written to do the same.
Please post some sample code in which you can instantiate the same class
twice, using the "new" operator, in which the _references_ of both
instances are identical.
I think you guys are right and I'm wrong on this one. At least I can't
get a class to do this (see my attempt below). I thought that
String.Intern would cause a single instance of my Empty field to be
created, but that doesn't seem to be the case, and I don't understand why.
The String class is integrated right into the CLR so that it knows the
exact layout of the class' fields (quoting Jeffrey Richter here), so I
bet if you look at the IL generated that there is special "string code"
that checks the one instance of the Empty field. But I'll admit I'm a
little puzzled on this one too.
No. The reference contained by the variable str1 is being replaced by a
new reference, which is what Jon wrote. However, the original string is
not replaced. For example:
string str1, str2;
str2 = "test string";
str1 = str2;
str1 = str1.Remove(0, str1.Length);
The original string "test string" still exists. It was not replaced. A
new string instance was created by the Remove() method, and the
reference to this new instance was assigned to str1.
I didn't say that the str2 was replaced or that str1's old string value
was replaced. Here's what Jon said:
and I said:
Well, the value of str1 was replaced with a reference to a different
instance. The string itself wasn't being replaced.
Indeed, str1 is replaced by a new instance of a string object, the one
returned by the call to str1.Remove. And the reference to the string
that str1 used to point to is decremented by 1 so when it goes to 0 the
GC can have at it.
None of this, however, explains why the Remove() method doesn't simply
return the string.Empty instance, rather than actually create a brand
new instance that happens to have data equivalent to string.Empty.
I see. I guess for the same reason that the char[] constructor behaves
the way it does, because it is documented to. This is in the Remove
documentation: "returns a new String that is equivalent to this instance
less count number of characters." Notice there is no verbiage describing
special-casing around a string that happens to come back as empty; it
says it *always* returns a new string. Which makes sense to me given the
immutable nature of strings. I don't think I'd want the Remove method to
have that special case, but I can see it's usefulness (in terms of
performance) for the char[] constructor.
-----------------------------------------------------------------
Here's the code I tried:
using System;
namespace MyStringTest
{
class Program
{
static void Main()
{
MyString x = new MyString(new char[] { });
MyString y = new MyString(new char[] { });
Assert("case 1a", true, ReferenceEquals(x, y));
Assert("case 1b", true, ReferenceEquals((object)x, (object)y));
string s1 = new string(new char[] { });
string s2 = new string(new char[] { });
Assert("case2", true, ReferenceEquals(s1, s2));
string s = "Hello";
Assert("case2", true, ReferenceEquals("Hello", s));
string h1 = "Hel";
string h2 = "lo";
Assert("case3a", false, ReferenceEquals("Hello", h1+h2));
Assert("case3b", true, ReferenceEquals("Hello", string.Intern(h1+h2)));
}
static void Assert(string name, bool expected, bool actual)
{
Console.Write(name + " - ");
Console.WriteLine(expected == actual ? "success" : "failure");
}
}
public class MyString
{
public static readonly string Empty = string.Intern("");
string _s;
public MyString(char[] ch)
{
if (ch == null || ch.Length == 0)
_s = Empty;
else
_s = new string(ch);
}
public bool IsEmpty
{
get
{
return _s == Empty;
}
}
}
}