1.1 值类型和引用类型
C# 中的类型一共分为两类,一类是值类型(Value Type),一类是引用类型(Reference Type)。值类型包括了结构和枚举,引用类型则包括了类、接口、委托等。还有一种特殊的值类型,称为简单类型(Simple Type),比如 byte,int 等,这些简单类型实际上是 BCL 基类库类型的别名。比如,声明一个 int 类型,实际上是声明一个 System.Int32 结构类型。
所有的值类型都隐式地继承自 System.ValueType 类型(注意 System.ValueType 本身是一个类类型)。之所以说是“隐式地”,是因为在 C# 代码中,是看不到这个继承关系的,这个关系只有通过 MSIL 代码才可以看到。System.ValueType 类型和所有的引用类型都继承自System.Object 基类。
说明:
栈(stack)是一种后进先出的数据结构,在内存中,变量会被分配在栈上来进行操作。堆(heap)是用于为引用类型的实例(对象)分配空间的内存区域,在堆上创建一个对象,会将对象的地址传给栈上的变量(反过来叫变量指向此对象,或者变量引用此对象)。
1.1.1 值类型
当声明一个值类型的变量(Variable)的时候,变量本身包含了值类型的全部字段,该变量会被分配在线程堆栈(Thread Stack)上。
1.1.2 引用类型
当声明一个引用类型变量,并使用 new 操作符创建引用类型实例的时候,该引用类型的变量会被分配到线程栈上,变量保存了位于堆上的引用类型的实例的内存地址。变量本身不包含任何类型所定义的数据。
1.1.3 简单类型
当使用“==”对引用类型变量进行比较的时候,比较的 是它们是否指向堆上同一个对象。,对于string类型,对它们的比较实际上比较的是值,而不是引用(string是 一种特殊的引用类型,它的特殊性在于它是不可变类型)
举个例子:
string a = "123456"; string b = "123456"; if(a == b) Console.WriteLine("a Equals to b");
1.1.4 装箱和拆箱
简单来说,装箱就是将一个值类型转换成等价的引用类型。它的过程分为这样几步:
1)在堆上为新生成的对象实例分配内存。该对象实例包含数据,但它没有名称。
2)将栈上值类型变量的值复制到堆上的对象中。
3)将堆上创建的对象的地址返回给引用类型变量。
举个例子:
int i = 1; Object boxed = i; Console.WriteLine("Boxed Point: " + boxed);
而拆箱则是将一个已装箱的引用类型转换为值类型:
需要注意的是:拆箱操作需要显示声明拆箱后转换的类型。它分为两步来完成:
1)获取已装箱的对象的地址。
2)将值从堆上的对象中复制到堆栈上的值变量中。
举个例子
int i = 1; Object boxed = i; int j; j = (int)boxed; // 显示声明拆箱后的类型
Console.WriteLine("UnBoxed Point: " + j);