C# 中的值类型和引用类型

C# 中所有的数据类型不是值类型就是引用类型。本文就这两种类型在变量定义、赋值、和作为函数参数传递时的不同之外作简单讨论。

值类型是使用对象实际值来表示对象的数据类型。 如果向一个变量分配值类型的实例,则该变量将被赋以该值的全新副本。

引用类型是使用对对象实际值的引用(类似于指针)来表示对象的数据类型。 如果为某个变量分配一个引用类型,则该变量将引用(或指向)原始值。 不创建任何副本。(来自MSDN)

值类型包括:

  1. 所有的数值类型
  2. Boolean、Char 和 DateTime
  3. 所有的结构和枚举

引用类型包括:

  1. String
  2. 数组
  3. 类、委托和接口

引用类型和值类型有不同的编译时规则和不同的运行时行为。

对于引用类型,创建对象时,将在堆上分配内存,变量只保存该对象的引用(类似于指针)。如 UriBuilder 是 System 命名空间中定义的一个类,Point 是 System.Drawing 命名空间中定义的一个结构。定义如下变量:

Point myPoint1 = new Point(20, 10); // Point 是一个结构
UriBuilder myUri1 = new UriBuilder("http://www.example.com"); // UriBuilder 是一个类

在上面的第一行代码中 CLR 将为变量 myPoint1 分配一块内存空间,而在第二行代码中 CLR 将要分配两块内存空间,一块存储 UriBuilder 对象,一块存储该对象的引用(myUri1)。第二行代码等价于:

UriBuilder myUri1; // CLR 为 UriBuilder 对象的引用 myUri1 分配一块内存空间
myUri1 = new UriBuilder("http://www.example.com"); // CLR 为 UriBuilder 对象分配一块内存空间

如果将上面定义的对象赋值给新的变量:

Point myPoint2 = myPoint1;
UriBuilder myUri2 = myUri1;

myPoint2 是一个结构,将被赋以 myPoint1 的一个全新的副本。而 myUri2 是一个类,只被赋以对 UriBuilder 对象的引用,myUri1 和 myUri2 都引用同一对象。

在 C# 中函数的参数默认是按值传递的,即当向函数传递参数时传递的是参数的隐式的副本。对于值类型的参数,传递的是该值的一个副本,对于引用类型的参数,传递的是该引用的一个副本。例如:

Point point = new Point(20, 5); // 值类型变量
UriBuilder myUri = new UriBuilder("http://www.example.com"); // 引用类型变量
Test(point, myUri); // Test 为下面定义的一个方法

void Test(Point p, UriBuilder uri)
{
	p.X = 10; // 不会影响 point,因为 p 是 point 的一个副本
	uri.Host = "www.example.org"; // 会影响 myUri因为 uri 和 myUri 引用的是同一对象
         uri = null; // 不会影响 myUri
}

将 uri 设置为 null 不会产生任何影响,因为 uri 只是 UriBuilder 对象引用的一个副本。

我们可以使用 ref 修饰符改变参数的传递方式,按引用传递时函数直接与调用者的参数交互。例如:

Point point = new Point(20, 5); // 值类型变量
UriBuilder myUri = new UriBuilder("http://www.example.com");// 引用类型变量
Test(ref point, ref myUri); // Test 是下面定义的函数

void Test(ref Point p, ref UriBuilder uri)
{
	p.X = 10; // 将会改变 point 的 X 值
	uri.Host = "www.example.org"; // 将会改变 myUri 的 Host 值
	uri = null; // 将会把 myUri 设置为 null
}
posted @ 2011-05-04 23:44  forgetu  阅读(1405)  评论(7编辑  收藏  举报