最近博客園上連續出現了幾篇關於堆VS棧 值類型VS引用類型的文章。
一個是關於C# 堆VS棧的,深入淺出,動圖清晰明了,鏈接如下
二是從Eric Lippert(Eric Lippert is a principal developer on the C# compiler team)的文章演變出的兩篇,鏈接如下
下面是我的思考和總結,希望能與大家分享和討論。
值類型和引用類型的區別
最近和同事一起面試時也經常問這個問題,被面的同學很多回答為“值類型存儲在棧里,引用類型存儲在堆里”,首先我們先不去深究這個說法是否准確(上面的文章里已經說的很清楚)。
值類型和引用類型的區別是存儲位置的不同嗎?
Eric Lippert給出的答案----值類型和引用類型的區別在語義層面,與存儲位置無關。存儲位置是C#編譯運行時的分配,是實現細節。
或許C# compiler的未來版本中,值類型也可能不存儲在棧里;
或者某一個系統平台中並不存在堆和棧的概念。
結論
“值類型和引用類型的區別,與存儲位置無關”。
值類型和引用類型區別是什么呢?
Eric Lippert說 - 值類型和引用類型的區別在語義層面。要怎么理解?我們思考的視角應該是C#語言語義和使用上。
結論
“值類型傳遞的是值(不同的實例,互不影響),引用類型傳遞的是引用(同一個實例,互相影響)”
如同一句廢話!那我們試着換幾種方式來描述(可能不准確)
1. 值類型是私有的,是持有者自己的東西;引用類型是共享的,大家共有的東西。
2. 值類型是多實例的,每次傳遞都創建新實例;引用類型是單實例的,每次傳遞都是同一個實例,如設計模式中的單例。
3. 值類型的生命周期和持有者相同;引用類型的生命周期和持有者不同。(有關生命周期的言論)
為什么在討論值類型和引用類型時,總會出現堆和棧?
如何實現值類型和引用類型的存儲?
有兩種存儲方式可供選擇
一 直接存儲的優點是性能高,缺點是共享不方便。(棧或者堆上)
二 間接存儲的優點是共享方便,缺點是多了一次跳轉,有性能損失。(堆上)
我們要的關注哪些問題?性能,共享方式(生命周期)
值類型是不共享的,它的生命周期和持有者相同,所以可以直接存儲,如果間接存儲,會多一次跳轉,沒有意義的性能損失。
引用類型是共享的,它的生命周期和持有者不同,所以采用間接存儲,如果直接存儲,是很難實現共享和生命周期的不同。
結論
值類型是直接存儲,引用類型是間接存儲。是基於實現的考量,不是值類型和引用類型的區別。
生命周期是從實現角度思考的推論,也不是值類型和引用類型的區別。
為什么存在兩種類型(值類型和引用類型),而不僅僅是一種類型(值類型或者引用類型)?
思考后發現,所有值類型都可以被引用類型所替代,那為什么要有值類型呢?沒有得出理想的答案,推測有兩種可能
一 歷史傳承。
二 基於性能的考量。這個應該是實現級別的事情,為什么被暴露在語言級別上?沒有辦法解決嗎?(對於值來說,引用類型多了一次跳轉;對於引用來說,值的傳遞多了一次深克隆)
何時用值類型(struct),何時用引用類型(class)?
在工作中很少(幾乎沒有)使用struct,因為性能上的收益遠遠無法彌補維護成本的損失。
(不能要求每個開發人員都很了解struct和class的不同,並在修改代碼時意識到使用的是struct而不是class)
總結
值和引用類型的區別是語言和使用級別的
值類型傳遞的是值(不同的實例,互不影響),引用類型傳遞的是引用(同一個實例,互相影響)
有關值類型和引用類型儲存在棧或者堆上的言論,是基於實現細節的思考,是當前的實現方式,不是值類型和引用類型的區別。
有關值類型和引用類型生命周期的言論,是基於實現細節的思考,是當前的實現方式,不是值類型和引用類型的區別。
值類型(struct)更多是性能上的收益,C#中定義的基礎值類型已經足夠,開發中盡量避免定義值類型(struct)。
期待看到不同的觀點和理由!