Struct與Class辨析

最美的回憶發表於2017-04-11

2010 .NET面試題整理之基礎篇》文中第十三題為:

==========================================

13、在dotnet中類(class)與結構(struct)的異同?

Class可以被例項化,屬於引用型別,是分配在記憶體的堆上的 

Struct屬於值型別,是分配在記憶體的棧上的。

==========================================

什麼時候dotnet中類與結構的異同變成一個是分配在堆上一個分配在棧上了?搜尋引擎一搜,網上全是這種說法,嚇一跳。順藤摸瓜,原來是出自《[你必須知道的.NET] 第四回:後來居上:class和struct》。

我個人不認同這種觀點,我認為它們的本質區別不是分配在棧和堆的問題,甚至跟分配在棧和堆關係不大,而是下面兩點:

· struct的例項 所佔記憶體大小是固定的,class例項 所佔記憶體大小是不固定的(因為可以繼承);當然,從另一個角度解讀就是class是可繼承的,而struct是不可以繼承的;這是一個硬幣的兩面;這是它們在儲存上的區別,你可以把struct當作閹割後的class。

· struct  預設 是傳值,但可以傳引用,class則預設傳引用,無法傳值這是它們在使用上的區別

下面展開說幾點:

(1)class也可能分配在棧上(特例)

Int32[] cache = new Int32[100];

cache一個陣列,是class,使用 new 關鍵字,它是分配在堆上的。

Int32* cache = stackalloc Int32[100];

當使用 stackalloc 它就是分配在棧上了。

值型別陣列是特例,但這一特例就夠了,表明class是可以分配在棧上的。一般的class是無法分配在棧上的,編輯器解釋說無法知道size。當然,你也可以認為陣列是一個特殊的型別,這個特例不算。實際上,這裡的陣列已經丟失了class的特徵了,你再也無法用陣列類去引用它了。如果去除這個特例,則class無法在棧上分配。但是,你也可以這樣理解——不是不能,而是不為,微軟目前還不想這樣幹。因為雖然class的size是不確定的,但一個class的例項存在一個最小size,只分配這個最小size的記憶體即可——C++就這樣乾的。微軟不支援,可能他們認為這樣沒必要,不必要搞這麼複雜。一般來說,能夠stackalloc 值型別陣列就足夠了。

(2)struct也可以分配在堆上

這個無須解釋。任何class裡面的struct都是分配在堆上的(通過stackalloc分配的陣列除外)。有沒辦法直接分配呢?我試了幾分鐘,沒發現把struct直接分配在託管堆上的方法,但卻可以通過Marshal.AllocHGlobal方法分配在非託管堆上。因此,有兩種方法可以struct分配在堆上:

(a)設struct是某個class的成員,可將struct分配在託管堆上

(b)使用Marshal或自己寫的記憶體分配器,可將struct分配在非託管堆上

(3)struct也可以傳引用

struct預設是傳值的,在安全環境下,使用ref關鍵字可以傳引用,在非安全環境下,使用指標可以傳引用。

(4)引入struct是為了解決效能問題——小粒度物件的傳值比傳引用效率高。同時,在非安全程式碼中,可以使用struct以及指標和非託管資源互動。

本文轉自xiaotie部落格園部落格,原文連結http://www.cnblogs.com/xiaotie/archive/2010/03/31/1701056.html如需轉載請自行聯絡原作者

xiaotie 集異璧實驗室(GEBLAB)


相關文章