C#详解值类型和引用类型区别
MSDN的这张图说明了CTS中各个类型是如何相关的。
1. 值类型
在C#中值类型的变量直接存储数据,而引用类型的变量持有的是数据的引用,数据存储在数据堆中。
C#的所有值类型均隐式派生自System.ValueType:
结构体:struct(直接派生于System.ValueType);
数值类型:
整型:sbyte(System.SByte的别名),short(System.Int16),int(System.Int32),long(System.Int64), byte(System.Byte),ushort(System.UInt16),uint(System.UInt32),ulong(System.UInt64),char(System.Char);
浮点型:float(System.Single),double(System.Double);
用于财务计算的高精度decimal型:decimal(System.Decimal)。
bool型:bool(System.Boolean的别名);
用户定义的结构体(派生于System.ValueType)。
枚举:enum(派生于System.Enum);可空类型(派生于System.Nullable泛型结构体,T?实际上是System.Nullable的别名)
2.引用类型(ReferenceType)
引用类型包括:数组,用户定义的类、接口、委托,object,字符串,null类型,类。
引用类型的变量持有的是数据的引用,数据存储在数据堆,分配在托管堆中,变量并不会在创建它们的方法结束时释放内存,它们所占用的内存会被CLR中的垃圾回收机制释放。
数组(派生于System.Array)
用户需定义以下类型:
类:class(派生于System.Object);
接口:interface(接口不是一个“东西”,所以不存在派生于何处的问题。接口只是表示一种contract约定[contract])。
委托:delegate(派生于System.Delegate)。
object(System.Object的别名);
字符串:string(System.String的别名)。
3.值类型与引用类型区别:
4.内存分配
值类型的实例经常会存储在栈上的。但是也有特殊情况。如果某个类的实例有个值类型的字段,那么实际上该字段会和类实例保存在同一个地方,即堆中。不过引用类型的对象总是存储在堆中。如果一个结构的字段是引用类型,那么只有引用本身是和结构实例存储在一起的(在栈或堆上,视情况而定)。
引用类型在栈中存储一个引用,其实际的存储位置位于托管堆。简称引用类型部署在托管推上。值类型总是分配在它声明的地方:作为字段时,跟随其所属的变量(实例)存储;作为局部变量时,存储在栈上。值类型在内存管理方面具有更好的效率,并且不支持多态,适合用做存储数据的载体;引用类型支持多态,适合用于定义 应用程序的行为。
注:堆栈(stack)是一种后进先出的数据结构。在内存中,变量会被分配在堆栈上来进行操作。堆(heap)是用于为类型实例(对象)分配空间的内存区域,在堆上创建一个对象,会将对象的地址传给堆栈上的变量(反过来叫变量指向此对象,或者变量引用此对象)。
5.装箱和拆箱
1)装箱就是将一个值类型转换成等值的引用类型
在堆上为新生成的对象(该对象包含数据,对象本身没有名称)分配内存。
将堆栈上值类型变量的值拷贝到堆上的对象中。
将堆上创建的对象的地址返回给引用类型变量(从程序员角度看,这个变量的名称就好像堆上对象的名称一样)。
2)拆箱就是将一个引用类型转换成等值的值类型
将引用类型变量堆上的值拷贝到栈上面。