C# 值类型和引用类型的区别
简介
前几年的时候只是在上学的时候知道值类型是存在栈中,引用类型存在堆中,但是到底为什么这么存储以及这么存储的好处是什么不太理解,正好今年开始记录博客,以及看了一些书上的内容记录一下。
值类型
类型 | 默认值 | 说明 |
byte | 0 | 8位无符号的整数(0~255) |
sbyte | 0 | 8位有符号的整数(-128~127) |
long | 0 | 64位有符号整数(-2 |
ulong | 0 | 64位无符号整数 |
short | 0 | 16位有符号整数 |
ushort | 0 | 16位无符号整数 |
int | 0 | 32位有符号整数 |
uint | 0 | 32位无符号整数 |
float | 0.0 | 表示一个单精度浮点数字 |
double | 0.0 | 表示一个双精度浮点数 |
decimal | null | 28位精度 |
char | '\0' | 16位字符 |
bool | false | true或者false |
struct | null | 结构体 |
enum | null | 枚举类型 |
1.值类型隐式的继承了(System.ValueType),值类型一般是存放在栈当中,实例化一个值类型的时候在栈中开辟一个空间的时候已经包含了本身的字段,所以性能来说要比引用类型快(引用类型在存放的是指向对象数据的内存地址),并且值类型由于不存放在堆当中,不被gc的管控,因此值类型的使用缓解了托管堆的压力,并且减少了应用程序生存期内的垃圾回收次数。
当如下创建一个值类型的时候,会在相应的栈当中添加创建一个num类型值为0的实例。因为这些简单类型的定义如果按照new 的语法进行创建的话比较麻烦,所以编译器给给这些语法进行了优化可以直接写成int num=0;
public class Test { int num; }
引用类型
类型 | 默认值 | 说明 |
class | null | 类 |
object | null | 所有类型都派生自object |
string | null | 字符串 |
interface | null | 接口 |
array | null | 数组 |
delegate | null | 委托 |
1.引用类型的数据都是存放在堆当中,在使用new操作返回的是对象的内存地址,
static void Main(string[] args) { string str = "测试"; Console.ReadLine(); }
2.下面我们改变下str的值,引用类型不会去改变已经存在内存当中的数据,如果现在我们把str的值改为一个“test”,实际上他会在堆当中创建一个新的内存区域,并且把str的指针指向新创建的这个对象,原来的那个数据“测试”,在经过gc扫描的时候,发现并没有任何引用的话,gc会释放掉。
static void Main(string[] args) { string str = "测试"; str="Test"; Console.ReadLine(); }
3.有的时候在开发的时候,我们分不清楚为什么要给string类型初始化的时候设置string.Empty,而不是""呢,那是因为string.empty只是创建了一个空的对象,但是""的话是去内存开辟了相应的空间,所以如果一个在开始不清楚变量值得时候,可以尽量采用string.Empty来创建。
static void Main(string[] args) { string str =string.Empty; string st=""; Console.ReadLine(); }
总结
值类型:
- 栈中存储具体的值,减少缓解了托管堆的压力,并且减少了应用程序生存期内的垃圾回收次数。
- 值类型在被当做参数传递的时候,实际上是对值类型的字段进行复制,对性能有所影响。
- 值类型不一定都存在栈中,如果一个类(class)当中包含了值类型(int num),那么这个num实际上是存储在堆上的。
- 值类型都是密封类
引用类型:
- 引用类型存在堆当中,new 获取的实际上是指向对象的内存的地址。
- 引用类型当做参数传递的时候,传递的是引用的地址,而不是引用。
- 将引用的变量赋值给另一个变量,实际上是复制的内存地址,两个变量指向的相同的对象。
- 引用类型可以派生新类型。