值类型与引用类型

iamlcw / 2023-09-03 / 原文

值类型和引用类型

  在c#中有两种基本类型,它们分别是值类型和引用类型;而每种类型都可以分为以下类型

1.什么是值类型和引用类型

    值类型:所有的结构都是抽象类型System.ValueType的直接派生类,而System.ValueType本身又是直接从System.Object派生的。根据定义所知,所有的值类型都必须从System.ValueType派生,所有的枚举都从System.Enum抽象类派生,而后者又从System.ValueType派生。  所有的值类型都是隐式密封的(sealed),目的是防止其他任何类型从值类型进行派生。       

  引用类型:在c#中所有的类都是引用类型,包括接口

2.区别和性能

  区别

    值类型通常被人们称为轻量级的类型,因为在大多数情况下,值类型的的实例都分配在线程栈中,因此它不受垃圾回收的控制,缓解了托管堆中的压力,减少了应用程序的垃圾回收的次数,提高性能。

    所有的引用类型的实例都分配在托管堆上,c#中new操作符会返回一个内存地址指向当前的对象

    在创建个一个引用类型实例的时候需要考虑几个问题

         1.内存是在托管堆上分配的

    2.在分配每一个对象时都会包含一些额外的成员(类型对象指针,同步块索引),这些成员必须初始化

    3.对象中的其他字节总是设为零

    4.在分配对象时,可能会进行一次垃圾回收操作

  性能

    在设计一个应用程序时,如果都是应用类型,那么应用程序的性能将显著下降,因为这会加大托管堆的压力,增加垃圾回收的次数。

    虽然值类型是一个轻量级的类型,但是如果大量的使用值类型的话,也会有损应用程序的性能

值类型的装箱和拆箱操作

    什么是装箱:装箱就是将值类型转化为引用类型的过程,在装箱时编译器内部会处理以下事:

    1.在托管堆中分配好内存,分配的内存量是值类型的各个字段需要的内存量加上托管堆上所以对象的两个额外成员(类型对象指针,同步块索引)需要的内存量

    2.值类型的字段复制到新分配的堆内存中

    3.返回对象的地址,这个地址就是这个对象的引用

    什么是装箱:将已装箱的值类型实例转化成值类型的过程,拆箱不是直接将装箱过程倒过来,拆箱的代价比装箱要低的多,拆箱其实就是获取一个指针的过程。一个已装箱的实例在拆箱时,编译器在内部都干了下面这些事:

    1.如果包含了“对已装箱类型的实例引用”的变量为null时,会抛出一个NullReferenceException异常。

    2.如果引用指向的对象不是所期待的值类型的一个已装箱实例,会抛出一个InvalidCastException异常