|
在本文中,首先从介绍简单.net类型开始,然后迅速进入关于引用类型和数值类型的讨论。对所有的开发人员来说,熟练掌握引用类型和数值类型的应用差别尤其重要。在编写代码的过程中,如果对这两种类型使用不当会导致程序Bug并引起性能问题。
简单类型
某些常用的数据类型,许多编译器通过简单的语法就可以对它们进行处理。例如,在C#语言中,你可以使用下列语法来分配一个整型变量:
以下是引用片段: int a = new int(5); |
但是我敢肯定,你会觉得用这样的语法来声明和初始化一个整型变量很笨拙。好在许多编译器(包括C#编译器)允许你使用下面的语法来代替:
这就使代码的可读性更强。不论使用那一种语法,产生的中间语言时一样的。
凡编译器直接支持的数据类型称为简单数据类型。这些简单数据类型直接映射到基类库中存在的类型。例如C#中int类型直接映射到System.Int32。所以可以将下列两行代码与前面提到的两行代码是一样的:
以下是引用片段: System.Int32 a = new System.Int32(5); System.Int32 a = 5; |
表一是C#中简单数据类型与基类库中有关类型的对应表(其它语言也会提供类似的简单数据类型)
表 一
引用类型和数值类型
当从受管堆(managed heap)中分配对象时,new操作符返回对象的内存地址。通常将这个地址存储在一个变量当中。这种方式就是引用类型的变量,因为变量不包含实际对象的位,而是引用对象的位。
在处理引用类型时会有一些性能问题要考虑。首先,内存必须要从受管堆中分配,这样能强制垃圾回收。其次,引用类型总是通过指针来存取。所以每次引用堆中对象的成员时,为了实现期望的处理,必须要产生和执行收回指针的代码。这反而影响程序的大小和程序执行的速度。
除了引用类型外,实际的对象系统中还有轻量级的数值类型。数值类型对象不能在可回收垃圾的堆中分配,并且表示对象的变量不包含对象的指针,而是变量包含对象本身。因为变量包含着对象,处理对象也就不必考虑指针回收的问题,从而改进了性能。
下面的代码说明了引用类型和数值类型差别。Rectangle类型的声明使用了结构,而没有使用更普通的类。在C#中,使用结构声明的类型是个数值类型,而使用类声明的是引用类型。其它语言可能用不同的语法来描述数数值类型和引用类型,例如C++中使用_value修饰符。
以下是引用片段: // Reference Type (because of 'class')class RectRef { public int x, y, cx, cy; } // Value type (because of 'struct')struct RectVal { public int x, y, cx, cy; } static void SomeMethod { RectRef rr1 = new RectRef(); // Allocated in heap RectVal rv1; // Allocated on stack (new optional) rr1.x = 10; // Pointer dereference rv1.x = 10; // Changed on stack RectRef rr2 = rr1; // Copies pointer only RectVal rv2 = rv1; // Allocate on stack & copies members rr1.x = 20; // Changes rr1 and rr2 rv1.x = 20; // Changes rv1, not rv2} |
回顾前面讨论简单类型时提到过的代码行:
以下是引用片段: System.Int32 a = new System.Int32(5); |
编译这个语句时,编译器发觉System.Int32是数值类型并优化产生的中间语言(IL)代码,以便使这个“对象”不从堆中分配;而将这个对象放到线程堆栈的局部变量a中。
|