CLR via C#-基元类型、引用类型和值类型

本文详细介绍了编程中的基元类型,包括编辑器直接支持的数据类型及其转换规则,强调了编译器在基元类型间的隐式和显式转换。接着探讨了引用类型和值类型的区别,如内存分配、性能影响以及它们各自的优缺点。值类型通过System.ValueType派生,并需要遵循特定的设计原则以保证性能。文章还详细阐述了值类型的装箱和拆箱过程,以及这两个操作对性能的影响。最后,讨论了如何通过StructLayoutAttribute特性来控制类型字段的内存布局。

理解不同的类型之间的区别,可以提高编码效率。


基元类型(primitive type)
编辑器直接支持的数据成为基元类型
int => System.Int32
sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、bool、decimal、string、object、dynamic
可以理解为方便我们编码,编辑器可以自动转换 System.XX 类型

编译器可以执行基元类型之间的隐式或显式转换

int i = 5;
long l = 5;
byte b = (byte)5; //可能丢失精度时需要显式转换

checked 和 unchecked 基元类型操作
可以检测算术运算的结果是否溢出


引用类型和值类型
CLR 支持的两种类型
使用引用类型必须要留意一些性能问题
a.内存必须从托管堆分配
b.堆上分配的每个对象都有一些额外成员,这些成员必须初始化
c.对象中的其他字节总是设为0
d.从托管堆分配对象时,可能强制执行一次垃圾回收

如果所有的类型都是引用类型,程序性能将显著下降
为了提升简单和常用类型的性能,CLR 提供了值类型的轻量级类型
值类型的实例一般在线程栈上分配(也可以作为引用类型的字段)
(创建一个线程会分配1MB的栈空间)
值类型的实例不包含指向实例的指针,包含了实例本身的字段。并且不受垃圾回收器的控制
值类型派生自 System.ValueType (派生自 System.Object)

值类型可以提供更好的性能,但是最好满足以下要求:
a.类型具有基元类型(都是简单类型)
b.类型不需要从其他任何类型继承
c.类型也不派生出其他任何类型
值类型实例大小也在考虑范围之内,实参默认以传值方式传递,造成对值类型实例中的字段进行复制,对性能造成损害。在作为返回值时,也会复制。那最好满足以下条件:
a.类型的实例较小(16字节或者更小,一个寄存器能存下)
b.类型的实例较大时,不作为方法实参或者返回值

值类型和引用型的一些区别
a.会发生装箱
b.需要自己重写 Equals 和 GetHashCode 方法,确保在字段值完全匹配的前提下返回 true
(System.ValueType 已经做了这处理,但也需要自己实现)
c.不能在值类型中引入任何新的虚方法,所有的方法都不能是抽象的
d.引用类型默认是null,如果访问对象会报出空异常。值类型总是包含基础类型的一个值,所有成员都初始化为 0,访问不会造成空异常
e.将值类型变量赋值给另一个会进行逐字段的复制。引用类型只复制内存地址,所以两个活多个引用类型可以引用堆中的同一个对象
f.未装箱的值类型不在堆上分配,一旦定义了该类型的一个实例方法不再活动,为他们分配的存储就会被释放

可以通过 StructLayoutAttribute 特性,告诉 CLR 按什么方式排列类型的字段
a.LayoutKind.Auto:自动排列字段
b.LayoutKind.Explicit:保持自己的字段布局
c.LayoutKind.Explicity:利用偏移量在内存中显示排列

如果不指定,编辑器会自动选择,一般引用类型是a,值类型是b


值类型的装箱和拆箱
比如使用 ArrayList.Add 就会发生装箱,具体做了什么呢?
a.在托管堆中分配内存(值类型各字段所需的内存+类型对象指针+同步块索引)
b.值类型的字段复制到新分配的堆内存
c.返回对象地址,值类型变成了引用类型

从 ArrayList 取数据时,就回发生拆箱,具体做了什么呢?
a.如果变量为 null,抛出空异常
b.如果不是空,但不是所需的值类型,抛出 InvalidCast 异常

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值