1. 基元类型
编译器直接支持的数据类型,称为基元类型(primitive type)基元类型直接映射到 Framework 类库(FCL)中存在的类型,例如: int 直接映射到 System.Int32类型,string 映射到 System.String等
System.Int32 a= new System.Int32(); // 代码1 int a=0; // 代码2 基元类型
无疑代码2的可读性要高于代码1,同时简化了编程,在生成 IL代码时,完全一致。
- 在开发中有时会遇到这样的困惑,是使用string 类型还是 String, 由于C#的 string(关键字)直接映射到 System.String(一个FCL 类型),所以两者没有区别,都可以使用,类似地还有int,一些开发人员说应用程序在32位操作系统上运行时,int 代表32位,在 64位上运行时,int代表64位,这个说法是完全错误的,在C#中 int 始终映射到 System.Int32,所以不管在什么操作系统上运行,int 代表的都是 32位整数。
- 在C#中 long 映射到 System.Int64,但在其它编程语言中,long 类型可能映射到 Int16 或 Int32, 例如,C++/CLL 就将long 视为Int32
- FCL 的许多方法都将类型用作方法名的一部分,例如,BinaryReader 类型提供的方法包括 ReadBlooean, ReadInt32, ReadSingle 等 ,System.Convert 提供的方法包括 ToBoolean, ToInt32,ToSingle 等
对基元类型执行许多算术操作都可能造成溢出,这时可用 check 或 uncheck 关键来进行基元操作,这样生成的代码在执行时会稍慢些,因为 CLR 会检查这些运算,判断是否会发生溢出。如果发生溢出,则抛出一个 OverflowException 异常。
UInt32 a = unchecked((UInt32)(-11)) // ok Byte b=100; b=checked((Byte)b+200)) //抛出 OverflowExceptoin异常
这是因为 b 和200 首先转换成 Int32 位值,然后回到一起,结果是300,然后,因为显式类型转换的原因,300转换成一个Byte,则会导出 OverflowException 异常,如果Bytej是在 checked 外面转换的,则不会发生异常
b=(Byte)checked(b+200) // ok, b包含 44
2. 引用类型
任何称为“类”的类型,都是引用 类型,引用类型直接派生自 System.Object,引用类型总是从堆托管上分配的,C#的new 操作符返回对象的内在地址--也就是指向对象数据的内在地址,使用引用类型时,必须注意一些性能问题,首先要考虑到以下事实:
- 内在必须从托管堆上分配
- 堆 上分配的一个对象都有一些额外的成员,这些成员必须初始化
- 对象中的其它字节(为字段而设)总设置为零
- 从托管堆上分配一个对象时,可能强制执行一次垃圾回收操作
3.值类型
所有的值类型都称为结构或枚举。结构都是抽象类型 System.ValueType 的直接派生类,System.ValueType 本身又直接派生自 System.Object,根据定义,所有的值类型都必须从 System.ValueType 派生,所有的枚举都从 System.Enum抽象类派生,System.Enum 又是从System.ValueType 派生。CLR和所有编程语言都给予枚举特殊待遇。
- 值类型总是在栈上分配内存。
- 将一个值类型变量赋值给另一个值类型变量,会执行一次逐字段复制,将引用类型的变量赋值给另一个引用类型的时,只复制内存地址。
- 基于上一条,两个或多个引用 类型的变量能引用堆中的同一对象,所以对一个变量执行的操作,可能影响到另一个变量引用的对象,相反,值类型的变量是自成一体的对象,对一个值类型的变量执行的操作不可能影响到另一个值类型变量。
- 由于末装箱的值类型不在堆上分配 ,所以一旦定义了该类型的实例方法不再处于活动状态,为它们分配的存储就会被释放,这意味着值类型的实例在其内在被回收时,不会通过Finalize 方法收到一个通知。
4. 值类型的装箱与拆箱
值类型对象的装箱过程:
- 在托管堆中分配好内存,分配的内存量是值类型的各个字段需要的内存量加上托管堆的所有对象都有的两个额外成员(类型对象指针和同步块索引)需要的内存量
- 值类型的字段复制到新分配的堆内存
- 返回对象的地址。现在,这个地址是对一个对象的引用 ,值类型现在是一个引用类型。
值类型对象的拆箱过程:
- 如果包含了“对已装箱值类型实例的引用”的变量为null,就抛出一个 NullReferenceException 异常
- 如果引用指向的对象不是所期待的值类型的一个已装箱实例,就抛出一个 InvalidCastException 异常
末装箱的类型比引用类型更“轻”,主要归结于以下原因:
- 它们不在托管堆上分配
- 它们没有堆上每个对象都有的额外成员,也就是一个“类型对象指针”一个“同步块索引”