String 在.net 2.0 中的定义为:
[SerializableAttribute]
[ComVisibleAttribute(true)]
public
sealed class String : IComparable, ICloneable, IConvertible,
IComparable<string>, IEnumerable<string>, IEnumerable,
IEquatable<string>
毫无疑问,string为引用类型,但在使用过程中它又表现出一些值类型的特点。
1.string 重载了 ==、!= 运算符
//
重载
==
运算符
public
static bool operator ==(String a, String b)
{
return Equals(a, b);
}
//
重载
!=
运算符
public
static bool operator !=(String a, String b)
{
return !Equals(a, b);
}
string 对象重写了 Equals 方法,实现字符串的比较。
2.string 是只读的。
不能修改该对象实例的值,实际操作中对该对象实例的修改返回的是该对象的新的实例。而 StringBuilder 则是在原对象实例基础上的修改,这就是修改操作时 StringBuilder 效率高于 string 对象的原因。string 对象保留在堆上,而不是堆栈上,当相同的字符串赋值到两个string变量时,会得到相同内存中的字符串的两个引用,这样做的目的是提高效率,对其进行了特殊优化,字符串是经常用的基本数据类型并且是只读类型,没有必要把相同的字符串在内存中保留多个复本。
//
相同的字符串赋值到两个
string
变量时,会得到相同对内存中的字符串的两个引用
string
aa = "hello";
string
bb = "hello";
Console
.Write(string.Equals(aa, bb)); //true
Console
.Write(string.ReferenceEquals(aa, bb)); //true
//
改变字符串,获得新的实例
bb += "o";
Console
.Write(string.Equals(aa, bb)); //false
Console
.Write(string.ReferenceEquals(aa, bb)); //false
3.参数传递中的假象
疑问:当字符串变量作为参数传递时并未改变它的值。
string
str = "hello";
Console
.WriteLine(str); //hello
Console
.WriteLine(ChangeString(str)); //helloo
Console
.WriteLine(str); //hello
Console
.WriteLine(string.Equals(str, str)); //true
Console
.WriteLine(string.Equals(str, ChangeString(str))); //false
//
改变字符串
private
string ChangeString(string str)
{
str += "o";
return str;
}
原因是字符串是只读的,改变时创建了一个新的对象。这时应该明确的标识 ref 或 out。
这不是个别现象,引用类型作为参数传递欲改变对象本身时都应明确的标识 ref 或 out,这让我想起当初学C语言是指针作为参数传递时为什么没有改变原指针本身:应该传递的是指针的地址。
下面的代码举例比较明显:
//
测试
Test1
tst = new Test1();
tst.aa = 1;
ChangeClass(tst);
Console
.WriteLine(tst.aa); //1
ChangeClassRef(ref tst);
Console
.WriteLine(tst.aa); //3
//
测试值传递参数
public
void ChangeClass(Test1 tst)
{
tst = new Test1();
tst.aa = 2;
}
//
测试引用传递参数
public
void ChangeClassRef(ref Test1 tst)
{
tst = new Test1();
tst.aa = 3;
}
//
测试用类
public
class Test1
{
public int aa = 0;
}