对于值类型和引用类型,我们单从概念上来看,其区别只是值类型直接存储其值,而引用类型存储的是值的引用。 这两种类型在内存中存储的表现为:值类型存储在堆栈中,而引用类型存储在托管堆上。这种存储位置上的不同相应的,也会有不同的影响。 举例:
int j,i; i=20; j=i; 这里由于int是值类型的,所以上面的代码在内存中实际上开辟了两块空间存储的值20。 但考虑下面的代码。这段代码假定已经定义了一个类Test,Test是一个引用类型,它有一个int类型的成员变量Value:
Test x,y; x=new Test(); x.Value=30; y=x; Console.WriteLine(y.Value); y.Value=50; Console.WriteLine(x.Value); 要理解的重要一点是在执行这段代码后,只有一个Test对象。x和y都指向包含该对象的内存位置。因为x和y是引用类型的变量,声明这两个变量只保留了一个引用——而不会实例化给定类型的对象。两种情况下都不会真正创建对象。要创建对象,就必须使用new关键字,如上所示。因为x和y引用同一个对象,所以对x的修改会影响y,反之亦然。因此上面的代码会显示30和50。
如果变量是一个引用,就可以把其值设置为null,表示它不引用任何对象:
y=null; 在C#中,基本数据类型如bool,long都是值类型。如果声明一个long类型的变量,并给它赋予另一个long变量的值,在内存中就会有两个bool值,而它俩之间也没有必然联系,修改之中一个不会影响另一个。 大多数更复杂的C#数据类型,包括我们自己声明的类都是引用类型。其分配在堆中,生存期可跨多个函数调用。CLR执行一种精细的算法,来跟踪哪些引用变量仍是可以访问的,哪些引用变量已经不能访问了。CLR会定期删除不能访问的对象,把他们占用的内存返回给操作系统。这是通过垃圾回收器实现的。
把基本类型(如int和bool)规定为值类型,而把包含许多字段的较大类型规定为引用类型,C#这样设计是为了得到最佳性能。如果要把自己的类型定义为值类型,就应把它声明为一个结构。
|