2015-06-05 27 views
1

每一個人都知道,當我們將Struct(Value Type)傳遞給函數時,等待Object,發生裝箱。爲什麼CLR在完成從結構到對象的投射時做拳擊?

但是,結構自ValueType,其從對象繼承繼承...

實施例:

ArrayList a = new ArrayList(); 
Point p = new Point(5,6); 
a.Add(p); 

在此例如對盒裝和加入的ArrayList。但p已經是對象(如果你做「p是對象」,你會變成真)。編譯器是否檢查元數據以查看所有繼承層次以查看是否存在ValueType類以知道值類型是否應在堆棧上分配?而且,如果它在繼承ValueType的層次結構中找到它,它不會繼續看到內部類?

例如:編譯器檢查點元數據:從誰繼承Point? TypeValue!好吧,我不會繼續 - 是值類型

+0

,我認爲你應該專注於「引用」和「價值」。此外,價值類型沒有任何層次結構,因爲它們都是封閉的。 –

+2

值類型不是「在堆棧上分配的」。考慮一個整數數組。你相信數組中的所有整數都分配在堆棧上嗎?如果是這樣,那麼數組如何從分配它的方法中返回? –

+0

Eric Lippert先生,你的意思是,因爲堆棧是1 MB,它不能包含大數組?這是合乎邏輯的......所以,它被裝箱並保存在堆中? – zzfima

回答

1

但p已經是對象(如果你做「p是對象」,你會得到真實的)。

這並不意味着p的值已經是一個參考。

從C#規範7.10.10的is運營商 - 只要相關的部分,其中DPoint這裏,和Tobject

  • 如果T是引用類型,結果是如果DT是相同類型,則爲true;如果D是參考類型,並且存在從DT的隱式參考轉換,則或者D是typ值e和從DT的拳擊轉換存在

重點是我的 - 我們處於最後的情況。有一個裝箱轉換從Pointobject,這就是爲什麼is回報true ......但是,這並不意味着你可以Point類型的值轉換爲object型(即參考)的值,而不拳...

1

struct,以及所有其他值類型都是對象(Eric Lippert在此解釋:How do ValueTypes derive from Object (ReferenceType) and still be ValueTypes?)。

這並不意味着struct和所有其他值類型都是引用類型,它們被裝箱成引用類型。因此,object和'參考類型'是不一樣的! 012 Jonathan Skeet解釋說,隱式轉換在這裏也被考慮到了,所以這就是爲什麼你會看到這種行爲。

3

考慮結構的一些性質,第一:

  1. 在C#中,所有的結構從類System.ValueType,進而從System.Object繼承隱含繼承。
  2. 在C#,一個結構不能從任何其它結構繼承。這樣做的直接後果是,如果你有一個表達式,它的靜態已知類型是一個結構類型,則表達式的實例具有運行時類型等於靜態已知類型。 (簡單來說,如果你有一個int表達,則包含實例,絕對是準確的int

運行時類型檢查,情況很簡單:

  • 如果你問一個struct無論是object,則CLR中查找相關的元數據和層次結構中發現System.Object,所以它回答true

對於編譯時類型檢查,編譯器必須做出一些決定:

  • 如果一個結構表達被分配給值類型的存儲位置,則:
    • 如果類型不一樣,但有一個隱式轉換定義,轉換被調用,然後分配發生。
    • 如果沒有隱式轉換,但分配是有效的(目標結構類型是一樣的源結構類型),分配直接發生。
  • 如果一個結構表達被分配給參考類型的存儲位置,則:
    • 如果存在定義的隱式轉換,轉換被調用,然後分配發生。
    • 如果沒有隱式轉換,但分配是有效的(目標類是object或目標接口是該結構類型實現的接口),則發生裝箱操作和然後分配發生。

注意,通過存儲位置我的意思是變量,字段陣列插槽,方法的參數,等等。

因此,如果您將結構實例賦值給object變量,或者如果將結構實例作爲參數傳遞給期望表達式爲object的方法,則無關緊要(至少假設沒有過載決議涉及) - 在這兩種情況下,適用相同的規則。


(這種情況可能更復雜一點,但我相信上面覆蓋了一般的情況。)