在一篇博文上看到引用类型和值类型的区别如下
- 引用类型与值类型相同的是,结构体也可以实现接口;
- 引用类型可以派生出新的类型,而值类型不能;
- 引用类型可以包含null值,值类型不能(可空类型功能允许将 null 赋给值类型);
- 引用类型变量的赋值只复制对对象的引用,而不复制对象本身。而将一个值类型变量赋给另一个值类型变量时,将复制包含的值。
对于最后一条,经常混淆的是string。我曾经在一本书的一个早期版本上看到String变量比string变量效率高;我还经常听说String是引用类型,string是值类型,等等。
但后来我验证这是错误的,String类型和string类型是完全一回事,它们都是System.String。String是引用类型,也就是说其本质都是指向托管堆内存的指针。它和用户定义的引用类型实际是一回事,只有一个特性和用户定义的引用类型不一样,那就是所谓字符串对象的不可变性:
MSDN 字符串(C# 编程指南)字符串对象是不可变的:即它们创建之后就无法更改。 所有看似修改字符串的 String 方法和 C# 运算符实际上都以新字符串对象的形式返回结果。 在下面的示例中,当连接 s1 和 s2 的内容以形成一个字符串时,不会修改两个原始字符串。 += 运算符会创建一个包含组合内容的新字符串。 这个新对象赋给变量 s1,而最初赋给 s1 的对象由于没有其他任何变量包含对它的引用而释放,用于垃圾回收。
string s1 = "A string is more ";
string s2 = "than the sum of its chars.";
// Concatenate s1 and s2. This actually creates a new// string object and stores it in s1, releasing the// reference to the original object.
s1 += s2;
System.Console.WriteLine(s1);
// Output: A string is more than the sum of its chars.
由于“修改”字符串实际上是创建新字符串,因此创建对字符串的引用时必须谨慎。 如果创建了对字符串的引用,然后“修改”原始字符串,则该引用指向的仍是原始对象,而不是修改字符串时创建的新对象。 下面的代码说明了这种行为:
C#
string s1 = "Hello ";
string s2 = s1;
s1 += "World";
System.Console.WriteLine(s2);
//Output: Hello
再看一段示例程序
using System;
namespace CSTest
{
classProgram
{
unsafestaticvoid Main(string[] args)
{
string a = "原始值";
string b = a;
a = "修改值";
Console.WriteLine("string b="+b);
//string b = 原始值
String c = "原始值";
String d = c;
c = "修改值";
Console.WriteLine("String d=" + d);
//string d=原始值
Console.Read();
}
}
}
至于简化的名称其实是string
通过这篇文章,可以认识到两点:
1. 在C#中,string和String是完全一样的。
1. string是引用类型,只不过每次修改都会新建一个对象存储修改后的值,而不是在原始对象上修改。
6月2日更新
考虑通过以下代码将string包装为一个普通class
namespace Class1
{
public class testclass
{
private string str;
public testclass(string s)//构造函数
{
str = s;
}
public void set(string s)//修改字符串值
{
str = s;
}
public string get()//查询字符串值
{
return str;
}
}
}
后通过类似过程创建该类对象e和f
using System;
using Class1;
namespace CSTest
{
class Program
{
unsafe static void Main(string[] args)
{
testclass e = new testclass("原始值");
testclass f = e;
e.set("修改值");
Console.WriteLine("testclass f.str=" + f.get());
//testclass f.str=修改值
Console.Read();
}
}
}
此时C#展现了它类Java的一面,即:将一个对象赋值给另一个对象直接传递该对象的引用,而不是在托管堆上构造一个新的对象,这一点与C++不同。
C#提供的拷贝构造函数:
另有值得注意的一点是:在C#中,所有对象均继承自System.Object,这意味着所有对象都继承了一个“拷贝构造函数”,它的用法和C++有所不同,但这并不是重点。这个拷贝构造函数名叫MemberwiseClone,从名字上看起来,它是一个Memberwise的深拷贝,然而,我多次在Demo程序,MSDN,各类参考书籍上确认,它其实是个bitwise的浅拷贝(所以我很困惑为什么这个方法要叫MemberwiseClone)