1.4 通用类型系统

公共语言运行时的一个重要组成部分称为通用类型系统(Common Type System,CTS)。.NET在设计中博采众长,借鉴了各种主流编程语言的长处,包括C++、Java等。CTS定义了一个类型库,无论是Visual Basic.NET还是C#,它们的类型系统大体类似,因此.NET将各种不同的编程语言的数据类型进行抽象,就有了CTS。CTS为.NET平台上的各种编程语言提供了支持,这样虽然每种编程语言都有自己独特的类型系统,但编译后都会转换成CTS类型,此时就可以实现不同语言编写的程序之间的互操作性。例如,一个使用Visual Basic.NET编写的类的某个方法返回值类型为Integer,而C#中某方法的参数类型为System.Int32,这两个类型看起来似乎并不一致,好像不能相互调用,但由于Integer和System.Int32都对应CTS里的System.Int32类型,因此它们的类型实际上是一致的,故而,相互调用的拦路虎——类型不一致问题就迎刃而解了。

CTS类型主要分成两大类:引用类型和值类型,如图1-5所示。这两种类型之间也可以相互转换,方法是装箱(Boxing)和拆箱(UnBoxing),这两个概念将在1.4.2节详细介绍。

1.4 通用类型系统 - 图1

图 1-5 通用类型系统

从图1-5我们可以得出如下几点结论:

❑CTS类型不是引用类型就是值类型;

❑引用类型直接继承自Object对象,值类型继承自ValueType对象,而ValueType对象继承自Object对象;

❑CTS类型不管是否直接继承自Object对象,最终都继承自Object。

1.4.1 值类型和引用类型

值类型的变量直接存储数据,而引用类型的变量持有的是数据的引用,数据存储在堆中。例如我们常见到的String、Class、Interface等就是引用类型,而Char、Int32、Boolean等都是值类型。对于引用类型数据而言,由于数据存储在堆中,指向它的引用可能不止一个,因此当其他引用对数据进行修改时会影响到别的引用,如图1-6所示。

1.4 通用类型系统 - 图2

图 1-6 值类型和引用类型

在托管代码中,CTS为每种类型分配内存的方式只有两种:

❑分配在托管栈(Stack)中

❑分配在托管堆(Heap)中

前面已经提到,内存的分配是由CLR管理的。这两种方式的区别是:

❑分配在托管栈中的变量会在创建它们的方法返回时自动被释放,例如在一个方法中声明一个Char型的变量UserInput,它的值是C,当实例化它的方法结束时,UserInput变量在栈上占用的内存就会自动释放,如图1-6所示。

❑分配在托管堆中的变量并不会在创建它们的方法结束时释放内存,它们所占用的内存会被CLR中的垃圾回收(GC)机制释放。假如有一个String类型的变量Name,它指向托管堆中的数据"Hello World",当方法调用结束的时候,Name在托管栈上所占的内存会立即被释放,但它在托管堆上的数据"Hello World"还会依然存在,只不过此时可能没有变量引用指向它了,它将持续等待直到未来某个时候被GC回收并释放所占内存,如图1-6所示。

注意 系统GC的执行是不可预期的,也可以通过调用System.GC.Collect()来强制执行GC。