System.String是不可变的字符串吗?open in new window

在学习.net编程时,你一定有听到过这样的说法,System.String类是不可变字符串,也就是说你不能修改一个字符串的值。

比如以下这段代码

string s = "hello";
s = "world";

你并不是把s的值修改为world,而是生成了一个新的包含"world"的字符串,然后令s这个字符串的引用指向新的字符串,而原来的那个字符串就被抛弃掉了。

也正是基于String类的不可修改性,CLR采用了String Interning的技术来共享相同的字符串,以达到减少内存使用的目的。比如以下这段代码

string s1 = "hello";
string s2 = "hello";

if (Object.ReferenceEquals(s1, s2))
    Console.WriteLine("They are same");

s1s2其实指向相同的字符串。

那么是不是我们真的没有办法改变一个string的值呢?当然不是,在C++/CLI中,我们可以使用一些特殊的手段来达到我们的目的!

String^ s1 = "hello";
String^ s2 = "hello";

interior_ptr<Char> p = const_cast<interior_ptr<Char> >(PtrToStringChars(s1));

for(; *p; *p++='a');            

Console::WriteLine("{0} and {1}",s1,s2);

PtrToStringChars(String^)是一个定义在vcclr.h头文件中的一个helper function,它返回一个类型为interior_ptr<const Char>的内部指针,指向String实例内部所包含的字符串,之所以返回类型是interior_ptr<const Char>而不是interior_ptr<Char>是因为不希望我们改变String实例内的字符串,不过既然我们执意要这么做,那么就让我们用一个const_cast<T>来把这个const搞掉!

运行以上代码你会发现,s2现在的内容已经是aaaaa,而不是hello了。

当然,以上例子只是说明存在修改string实例的内容的可能,并不是鼓励大家这么做。虽然直接访问string实例内部的字符串,可能会带来一些性能上的好处。但是由于String Interning的存在,修改String实例内部的字符串是相当危险的行为,比如上面的那个例子中,你会发现,s1的输出也变成aaaaa了!

posted on 2004-12-07 22:15 Justin Shen 阅读(2415) 评论(4)


Feedback

1楼 2004-12-07 22:49 林

string s1 = "hello";
string s2 = "hello";

if (Object.ReferenceEquals(s1, s2))
Console.WriteLine("They are same");

s1s2其实指向相同的字符串。
你说错了,或者是你说的比较含糊
ReferenceEquals对于引用类型来说,完全和==等同
这点必须明白
对值类型来说,是把他们box成oject对象在进行==比较

2楼 [楼主] 2004-12-07 23:05 Justin Shen

string的==操作是重载过的,其结果取决于string引用所指向的字符串的内容是否相同,而不仅当指向同一个字符串时,才会返回true,这并不同于Object.
ReferenceEquals(),考虑以下的代码:

string s1 = "hello";
StringBuilder builder = new StringBuilder("hello");

string s2 = builder.ToString();

if (!Object.ReferenceEquals(s1, s2))
Console.Write("the two string references don't refer to the same string object, ");
if (s1 == s2)
Console.WriteLine("whereas the two strings themselves are equal.");

3楼 2004-12-07 23:07 林

不好意思我说错了
原谅

4楼 2004-12-08 09:23 fans1

C#也可以用指针达到修改的目的,不一定需要c++/cli来

Contributors: FHL