C#中的值类型和引用类型
0 |
值类型 |
引用类型 |
预定义类型 |
sbyte、byte、float、short、ushort、double、int、uint、char、long、ulong、decimal、bool |
object、string、dynamic |
用户自定义类型 |
struct、enum |
class、interface、delegate、array |
值类型和引用类型的异同
相同点
- 值类型和引用类型都是System.object的子类
- 值类型和引用类型都可以继承接口。例如:
interface Itest
{
void test();
}
struct StructTest:Itest
{
public void test()
{
...
}
}
不同点
- 值类型分配在堆或栈上,引用类型分配在堆上。若一个引用类型中的某个属性是值类型,这个值类型的属性是分配在堆上的。
- 所有的值类型都是隐式密封的(sealed)。不能继承int来构造自己的类型。
- 值类型的每一次赋值都会执行一次逐字段的复制,所有频繁赋值会造成性能上的压力。引用类型的赋值只是指针的传递,其实也是生成新的指针实例。
- 引用类型额外有类型对象指针和同步块索引,值类型是没有的,所以使用lock锁的对象不可能是值类型,因为值类型没有同步块索引。
装箱和拆箱
- 装箱:将值类型转化为引用类型的过程
- 拆箱:将引用类型转化为值类型的过程
- 装箱过程:
- 1.在托管堆中分配好内存,分配的内存量=值类型的各个字段需要的内存量+托管堆上所有对象的两个额外成员(类型对象指针,同步块索引)所需要的内存量
- 2.值类型的字段复制到新分配的堆内存中
- 3.返回对象的地址,这个地址就是这个对象的引用
- 拆箱过程:
- 1.获取已经装箱的值类型实例的指针
- 2.把获取到的值复制到栈
- 注:一个对象拆箱之后只能还原为原先未装箱之前的类型。
值类型和引用类型的应用场景
- 值类型可能更适用的场景
- 1.类型不会派生出其它类型,即不可能被继承
- 2.类型不需要继承其它类型
- 3.类型的实例比较小,且不会被作为方法的参数,不会被频繁的赋值
- 4.你永远不会用到类型释放时候的通知,因为引用类型利用析构函数可以利用其他手段可以得到释放时候的通知。
- 5.你的类型实例不会发生值的改变或者可以认为是readonly性质的
- 所有的值类型都从System.ValueType 派生,System.ValueType继承System.Object,但是System.ValueType 重写了Equals 和GetHashCode 方法