net里的值类型跟引用类型到底有啥不一样

咱们天天都得琢磨.NET里的值类型跟引用类型到底有啥不一样。话说回来,这俩类型在内存里的分配方式可完全不是一码事。值类型就像个直接放在手头的塑料袋,里面装的东西就是变量本身,比如 struct、int 或者 double;而引用类型更像是一个地址牌,牌上写着东西放在哪儿。变量本身就是这地址牌。你把一个值类型赋值给另一个变量,就相当于拿另一个塑料袋把东西原封不动地拷贝一份,各自独立;可引用类型一赋值,就是两张牌子都指同一处地方。性能上看,值类型因为不占堆内存且少了 GC 的折腾,小量的话自然跑得快点;要是值类型个头太大或者老是复制来复制去,那这“值拷贝”的代价也不小。反倒是引用类型,用来装大杂烩或者大家一起用的数据更合适。要是你在设计的时候没选对路,GC 压力和性能就可能跟着遭殃。 关于高频追问,先说 struct 吧。虽说它常被认为是在栈上待着的,但这也不一定准。如果 struct 当了别的类的成员字段,那它就得跟主类一样挤到堆上去。要是被装箱了(Boxing),它也得老老实实跑到堆里去。所以“值类型在栈上”只是个通常情况。再说装箱和拆箱(Unboxing),装箱就是给个 value 类型套上 object 或接口的外衣,这时候值就得被复制到堆里去;拆箱就是把这个外衣扒下来还原成原来的样子。这俩操作因为会搞出堆分配和额外的拷贝,所以会给 GC 添堵。要是你在方法调用或者搞集合操作的时候频繁遇到隐式装箱,那可得小心别拖了后腿。 为啥通常建议 struct 别搞得太大呢?因为不管是传参还是赋值,struct 都是整个儿复制一遍。要是里面塞了一大堆字段或者数组(比如 StringBuilder),这复制的成本就很可观了。所以大家一般喜欢用小而精、不轻易变动的 struct 来干活。至于 string,看着像值类型其实它是个引用类型。因为它是个铁了心不变的玩意儿(Type),你一改动就得再造个新对象。正因为这一点让它在多线程下特别安全。但要是你老是拿它去拼接字符串(Reference),就会生成好多临时对象把 GC 给累坏了。这时候最好还是换成 StringBuilder 来优化一下。