C#中值类型和引用类型

本文将介绍C#类型系统中的值类型和引用类型,以及两者之间的一些区别。同时,还会介绍一下装箱和拆箱操作。

值类型和引用类型

首先,我们看看在C#中哪些类型是值类型,哪些类型是引用类型。

值类型:

  • 基础数据类型(string类型除外):包括整型、浮点型、十进制型、布尔型。  
    • 整型(sbyte、byte、char、short、ushort、int、uint、long、ulong )
    • 浮点型(float 和 double )
    • 十进制型(decimal )
    • 布尔型(bool )
  • 结构类型(struct)
  • 枚举类型(enum)

引用类型:

  • class、interface、delegate、object、string、Array

默认值

变量的初始化中,都会有一个默认值,在C#中,我们可以通过default关键字去查看某个类型的默认值。

通过default(int)可以看到,int的默认值是0,default(bool)显示布尔类型的默认值是false。

对于所有的引用类型,默认值都会是null。

注意,这里有个特殊的情况就是结构struct,如果对一个结构进行default操作,我们将得到每个结构成员的初始值状态。也就是说,值类型成员赋予值类型的默认值,引用类型成员赋予引用类型的默认值。

简单对比值类型和引用类型

下面,我们通过一个简单的例子看看。假设有一个Point类型,有x和y两个坐标成员。

同样是下面一段代码

Point p1 = new Point(5,9);
Point p2 = p1;

 如果Point类型是通过结构struct实现,那么p2将会是p1的一个副本,也就是说任何一个的修改都不会影响另外一个;如果Point类型是通过类class实现,那么p2和p1的引用值将会指向同一个对象。

C#中值类型和引用类型

为了进一步了解值类型和引用类型,我们需要介绍一下栈和堆这两个基本概念。

栈和堆

当我们在32为系统上运行一个程序的时候,这个程序就会有一个4GB的进程运行空间。我们所要讨论的栈和堆就存放在这个4GB的空间中。

栈和堆的简介

在C#中,栈(Stack)是指堆栈;堆(Heap)是指托管堆,由.NET垃圾收集器自动管理。

这里就不对栈和堆进行详细的分析了,只是举一个简单的例子来大致描述栈和堆的工作原理。

C#中值类型和引用类型

从图中可以看到,局部变量在栈上的变化(入栈),当函数执行结束后,栈上的空间将会被清理;但是我们在堆上分配的空间始终从在,只能等待GC去帮我们清理不会被引用到的空间。

值类型和引用类型的存放

介绍过栈和堆之后,下面我们看看值类型和引用类型是怎么存放的。

对于值类型的变量,这个变量本身就代表这个值类型的值;但是,对于引用类型的变量,这个引用类型的实例是在托管堆上分配的空间,而这个变量本身只是代表一个指向托管堆实例的引用(指针)

所以这里,我们可以对值类型和引用类型变量的存储有两个概括:

  • 引用类型永远存储在堆里
  • 值类型和引用(指针)永远存储在它们声明时所在的堆或栈里
    • 如果一个值类型不是在方法中定义的,而是在一个引用类型里,那么此值类型将会被放在这个引用类型里并存储在堆上

注意:根据上面第二点概括,可以得到"值类型一定存储在栈中"这个说法是错误的。例如,我们有一个Student类,在这个类中的Age属性是一个值类型,但是这个值类型是存储在Student类实例的空间中,也就是在堆上。

class Student
{
    public string Name { get; set; }
    public int Age{ get; set; }
} 

装箱和拆箱

由于C#中所有的数据类型都是由基类System.Object继承而来的,所以值类型和引用类型的值可以通过显式(或隐式)操作相互转换。

这里,可以将装箱和拆箱描述为:

  • 装箱是将值类型转换为引用类型
  • 拆箱是将引用类型转换为值类型

装箱/拆箱的内部操作

其实,在装箱和拆箱的过程中都对应一系列的转换,这里就通过下图表示了。

C#中值类型和引用类型

在值类型进行装箱时,生成的是全新的引用对象,这会有时间损耗,也就是造成效率降低。所以在C# 2.0中就引入了泛型来减少装箱操作和拆箱操作消耗。

总结

本文介绍了C#中的值类型和引用类型,以及栈和堆的基本概念。然后分析了值类型和引用类型在栈和堆中的存放。

同时,我们也了解到了:

  • 当使用引用类型时,我们是在和指向引用类型的引用(指针)打交道,而不是引用类型本身
  • 当使用值类型时,我们是在和值类型本身打交道

 

C#中值类型和引用类型

上一篇:GitHub for windows 使用方法


下一篇:C# 中的可变参数方法(VarArgs)