C# 堆VS栈 值类型VS引用类型

最近博客园上连续出现了几篇关于堆VS栈 值类型VS引用类型的文章。

 

一个是关于C# 堆VS栈的,深入浅出,动图清晰明了,链接如下

C#堆栈对比(Part One)

C#堆栈对比(Part Two)

C#堆栈对比(Part Three)

C#堆栈对比(Part Four)

二是从Eric Lippert(Eric Lippert is a principal developer on the C# compiler team)的文章演变出的两篇,链接如下

The Truth About Value Types

你真的了解C#中的值和引用吗?(上)

匹夫细说C#:不是“栈类型”的值类型,从生命周期聊存储位置

 

下面是我的思考和总结,希望能与大家分享和讨论。

值类型和引用类型的区别

最近和同事一起面试时也经常问这个问题,被面的同学很多回答为“值类型存储在栈里,引用类型存储在堆里”,首先我们先不去深究这个说法是否准确(上面的文章里已经说的很清楚)。

值类型和引用类型的区别是存储位置的不同吗

Eric Lippert给出的答案----值类型和引用类型的区别在语义层面,与存储位置无关。存储位置是C#编译运行时的分配,是实现细节。

  或许C# compiler的未来版本中,值类型也可能不存储在栈里;

  或者某一个系统平台中并不存在堆和栈的概念。

结论

  “值类型和引用类型的区别,与存储位置无关”。

 

值类型和引用类型区别是什么呢?

Eric Lippert说 - 值类型和引用类型的区别在语义层面。要怎么理解?我们思考的视角应该是C#语言语义和使用上。

结论

  “值类型传递的是值(不同的实例,互不影响),引用类型传递的是引用(同一个实例,互相影响)

    如同一句废话!那我们试着换几种方式来描述(可能不准确

    1. 值类型是私有的,是持有者自己的东西;引用类型是共享的,大家共有的东西。

    2. 值类型是多实例的,每次传递都创建新实例;引用类型是单实例的,每次传递都是同一个实例,如设计模式中的单例。

    3. 值类型的生命周期和持有者相同;引用类型的生命周期和持有者不同。(有关生命周期的言论)

 

为什么在讨论值类型和引用类型时,总会出现堆和栈?

如何实现值类型和引用类型的存储?

  有两种存储方式可供选择

    一 直接存储的优点是性能高,缺点是共享不方便。(栈或者堆上

    二 间接存储的优点是共享方便,缺点是多了一次跳转,有性能损失。(堆上

 

我们要的关注哪些问题?性能,共享方式(生命周期)

  值类型是不共享的,它的生命周期和持有者相同,所以可以直接存储,如果间接存储,会多一次跳转,没有意义的性能损失。

  引用类型是共享的,它的生命周期和持有者不同,所以采用间接存储,如果直接存储,是很难实现共享和生命周期的不同。

结论

  值类型是直接存储,引用类型是间接存储。是基于实现的考量,不是值类型和引用类型的区别。

  生命周期是从实现角度思考的推论,也不是值类型和引用类型的区别。

 

为什么存在两种类型(值类型和引用类型),而不仅仅是一种类型(值类型或者引用类型)?

思考后发现,所有值类型都可以被引用类型所替代,那为什么要有值类型呢?没有得出理想的答案,推测有两种可能

  一 历史传承。

  二 基于性能的考量。这个应该是实现级别的事情,为什么被暴露在语言级别上?没有办法解决吗?(对于值来说,引用类型多了一次跳转;对于引用来说,值的传递多了一次深克隆)

 

何时用值类型(struct),何时用引用类型(class)?

在工作中很少(几乎没有)使用struct,因为性能上的收益远远无法弥补维护成本的损失

(不能要求每个开发人员都很了解struct和class的不同,并在修改代码时意识到使用的是struct而不是class)

 

总结

值和引用类型的区别是语言和使用级别的

值类型传递的是值(不同的实例,互不影响),引用类型传递的是引用(同一个实例,互相影响)

有关值类型和引用类型储存在栈或者堆上的言论,是基于实现细节的思考,是当前的实现方式,不是值类型和引用类型的区别。

有关值类型和引用类型生命周期的言论,是基于实现细节的思考,是当前的实现方式,不是值类型和引用类型的区别。

值类型(struct)更多是性能上的收益,C#中定义的基础值类型已经足够,开发中尽量避免定义值类型(struct)

 

期待看到不同的观点和理由!

 

 

 

 

 

posted @ 2015-04-16 13:38  翱翔  阅读(1827)  评论(11编辑  收藏  举报