[英]Why do structs need to be boxed?
在C#中,任何用戶定義的struct
都自動成為
System.Struct
System.ValueType
和
System.Struct
的子類System.ValueType
是System.Object
的子類。
但是當我們為對象類型引用分配一些結構時,它會被裝箱。 例如:
struct A
{
public int i;
}
A a;
object obj = a; // boxing takes place here
所以我的問題是:如果A
是System.Object
的后代,編譯器不能將它上傳到對象類型而不是裝箱嗎?
結構是一種值類型。 System.Object
是引用類型。 運行時以不同方式存儲和處理值類型和引用類型。 要將值類型視為引用類型,必須將其設置為加框。 從低級別的角度來看,這包括將值從最初所在的堆棧復制到堆上新分配的內存,該內存還包含一個對象頭。 引用類型需要額外的頭來解析它們的vtable以啟用虛擬方法調度和其他引用類型相關的功能(請記住,堆棧上的結構只是一個值而且它沒有類型信息;它不包含任何類似vtable和can的內容不能直接用於解析動態調度的方法。 此外,要將某些東西視為引用類型,您必須有一個引用 (指針),而不是它的原始值。
所以我的問題是 - 如果A是System.Object的后代,是不是可以將它編譯為對象類型而不是拳擊?
在較低級別,值不會繼承任何內容。 實際上,正如我之前所說,它並不是一個真正的對象。 A派生自System.ValueType
,而System.ValueType
又派生自System.Object
是在編程語言(C#)的抽象級別定義的,而C#確實很好地隱藏了裝箱操作。 您沒有明確提及任何內容來設置值,因此您可以簡單地認為編譯器已為您“上傳”了該結構。 它使得值的繼承和多態的錯覺 ,而多態行為所需的工具都不是由它們直接提供的。
這是我更喜歡考慮它的方式。 考慮包含32位整數的變量的實現。 當被視為值類型時,整個值適合32位存儲。 這就是值類型:存儲只包含構成值的位,僅此而已。
現在考慮包含對象引用的變量的實現。 該變量包含一個“引用”,可以通過多種方式實現。 它可以是垃圾收集器結構的句柄,也可以是托管堆上的地址,或者其他什么。 但它可以讓你找到一個對象。 這就是引用類型:與引用類型變量關聯的存儲包含一些允許引用對象的位。
顯然,這兩件事完全不同。
現在假設你有一個object類型的變量,並且你希望將int類型變量的內容復制到其中。 你怎么做呢? 構成整數的32位不是這些“引用”之一,它只是一個包含32位的桶。 引用可以是進入托管堆的64位指針,也可以是垃圾收集器數據結構中的32位句柄,或者您可以想到的任何其他實現,但32位整數只能是32位整數。
那么你在這種情況下所做的就是選中整數:你創建一個包含整數存儲的新對象,然后存儲對新對象的引用。
只有當你想要(1)擁有統一的類型系統,並且(2)確保32位整數消耗32位內存時,才需要拳擊。 如果你願意拒絕其中任何一個,那么你就不需要拳擊; 我們不願意拒絕那些,所以拳擊是我們被迫忍受的。
“如果
struct A
是System.Object
的后代,那么編譯器不能將它上傳而不是裝箱嗎?”
不,僅僅因為根據C#語言的定義,在這種情況下“up-casting” 是裝箱。
C#的語言規范包含(在第13章中)所有可能的類型轉換的目錄。 所有這些轉換都以特定方式進行分類(例如數字轉換,參考轉換等)。
存在從類型S
到其超類型T
隱式類型轉換,但這些轉換僅針對“從類類型S
到引用類型T
”的模式定義。 由於struct A
不是類類型,因此無法在示例中應用這些轉換。
也就是說, A
((間接)從object
派生(雖然正確)這一事實在這里簡直無關緊要。 相關的是A
是結構值類型。
與模式“從值類型A
到其引用超類型object
”匹配的唯一現有轉換被歸類為裝箱轉換。 因此,從struct
到object
每次轉換根據定義都被認為是裝箱。
struct
是一種設計的值類型,因此在轉換為引用類型時需要加框。 struct
派生自System.ValueType
,它在術語派生自System.Object
。
結構是對象的后代這一事實並不意味着......因為CLR在運行時處理的structs
與引用類型不同。
在問題得到解答之后,我將提出與該主題相關的一些“技巧”:
struct
s可以實現接口。 如果將值類型傳遞給期望此值類型實現的接口的函數,則該值通常會被裝箱。 使用泛型你可以避免拳擊:
interface IFoo {...}
struct Bar : IFoo {...}
void boxing(IFoo x) { ... }
void byValue<T>(T x) : where T : IFoo { ... }
var bar = new Bar();
boxing(bar);
byValue(bar);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.