NET CLR via C#读书笔记 - 第五章 基元,引用和值类型

1 基元类型

1.1 概念

基元类型可以简单理解为编译器本身支持的数据类型
例如:System.Int32  a  =  new System.Int32();  //值默认初始化为0
由于编译器内部对此做了处理,我们可以简单的将语句修改为:
int a = 0; 
编译器会将int在编译时映射为FCL中System.Int32类型

1.2 C#基元数据类型

C#基元类型FCL类型是否符合CLS说明
sbyteSystem.SBytefalse有符号8位值
byteSystem.Bytetrue无符号8位值
shortSystem.Int16true有符号16位值
ushortSystem.UInt16false无符号16位值
intSystem.Int32true有符号32位值
uintSystem.UInt32false无符号32位值
longSystem.Int64true有符号64位值
ulongSystem.UInt64false无符号64位值
charSystem.Chartrue16位Unicode字符
floatSystem.SingletrueIEEE 32位浮点值
doubleSystem.DoubletrueIEEE 64位浮点值
boolSystem.Booleantruefalse/true值
decimalSystem.Decimaltrue128位高精度浮点值,其中1位是符号位,96位是值本身(N),8位为比例因子(k),实际值为 ± N * (10 ^ k)
stringSystem.Stringtrue字符串(字符数组)
objectSystem.Objecttrue所有类型的基类型
dynamicSystem.Objecttrue对于CLR,dynamic与object完全一致,只是C#编译器在处理时会让dynamic定义的类型参与动态调度

1.3 基元类型的相关知识点

1.3.1 C#中允许基元类型执行安全的隐式转换

  安全转换指的是转换完成后不丢失精度和数量级。
  例如System,Int32向System.Int64进行转换,不需要显示转换,但是System.Int64向System,Int32则需要显示转换。
  补充知识点:不同的编译器对于同一源代码的转换会产生不同的结果,例如将 6.8f 转换为 System.Int32 类型时,有些编译器是向上取整,有些编译器是向下取整,C#编译器永远是执行向下取整,也就是对6.8f进行截断处理,只保留6这个整数部分。

1.3.2 checked和unchecked

  C#中基元类型的计算很多时候都会出现溢出情况,C#中支持使用checkedunchecked自定义是否检查溢出情况,C#编译器一般是默认检查溢出的,书写方式如下:

Byte b = 100;
b = checked((Byte)(b + 200)); //会抛出overflowException异常

也可以对某一代码块进行溢出检查:

checked{
	Byte b = 100;
	b = (Byte)(b + 200); 
}

特例说明:对基元类型System.Decimal使用checked和unchecked操作符是无效的,因为CLR本身不知道如何去处理System.Decimal值得IL指令,需要借助System.Decimal类型定义的Add等操作函数以及重载的+,-,*,/来进行实际的运算,所以System.Decimal类型运算相比于其它基元类型要慢,而且出现溢出时一定会抛出overflowException异常。

2 引用类型和值类型

2.1 引用类型和值类型定义

所有值类型都从System.ValueType派生,使用struct关键字定义。
所有引用类型都使用class关键字定义。

2.2 引用类型和值类型相关知识点

1、引用类型是从托管堆分配内存,值类型是在线程栈上分配空间。
  事实上引用类型使用过程中会执行以下四个步骤:
  ① 从托管堆分配内存
  ② 对象分配时都需要额外分配成员,且分配的额外成员必须初始化
  ③ 对象的其它字节也必须初始化为0
  ④ 从托管堆分配内存时,可能会强制执行一次垃圾回收机制
  值类型不受以上四个部分的困扰,值类型在线程栈上分配空间,且实例的字段也是直接存储,不需要分配额外的成员进行控制。值类型的存在减轻了托管堆的压力,而且显著减少垃圾回收次数,从而有效的提高程序的执行效率。

2、所有值类型都是隐式密封,避免作为其它类型(引用类型和值类型)的基类型。

3、值类型的装箱和拆箱机制
  装箱:将值类型的实例转化为引用类型。
  过程如下:
  ① 根据值类型的大小以及需要分配的额外成员(类型对象指针和同步块索引)在托管堆上分配内存。
  ② 将值类型的字段复制到第一步中分配的对内存中。
  ③ 返回对象地址(也就是分配堆的内存的地址)。

  拆箱:将已装箱的值类型对象转化为未装箱的值类型。
  过程如下:
  ① 获取已装箱的值类型对象各个字段的地址。
  ② 将各个字段包含的值复制到基于栈的值类型实例中。

结合上述说明,值类型装箱的代价要比拆箱的代价大,但两者都会对程序运行速度和内存资源造成影响,必要时要检查书写的代码是否存在频繁的装拆箱,并着手进行优化。

  以上内容为对《NET CLR via C#(第四版)》第五章内容的阅读笔记,只记录其中核心部分内容,书中还仔细描述了使用接口更改已装箱值,对象的同一性和一致性,对象哈希码以及dynamic等话题,如需要详细阅读请自行查阅本书第五章内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值