我還沒有研讀《CLR Via C#》之類的專著,只是心裡有了疑問,然後就去個人探究,下文多為猜想。更希望了解內幕的朋友告知C#後台真相。
我自個兒琢磨出來的結論:形如
1 // 原始聲明
2 struct People : IFormattable
3 {
4 public string ToString(string format, IFormatProvider formatProvider)
5 {
6 return ToString();
7 }
8 public override string ToString() { return Name; }
9
10 public string Name { get; set; }
11 }
的結構聲明,會被轉化為兩個後台聲明:
1 // 實際代碼中的struct People被映射成這個類型,虛方法聲明和接口繼承都無效了
2 struct PeoplePOD // : IFormattable
3 {
4 public string ToString(string format, IFormatProvider formatProvider)
5 {
6 return ToString();
7 }
8 public /* override */ string ToString() { return Name; }
9
10 public string Name { get; set; }
11 }
12 // 這是裝箱後的堆對象類型,任何試圖將原始struct轉化為object/ValueType/接口的轉型,都會被自動裝箱為這個類型的對象
13 class PeopleBox : IFormattable
14 {
15 public string ToString(string format, IFormatProvider formatProvider)
16 {
17 return ToString();
18 }
19 public override string ToString() { return Name; }
20
21 public string Name { get; set; }
22 }
代碼中出現的所有struct People,其實都是struct PeoplePOD,即放棄了虛函數和接口繼承後的純數據+非虛方法。沒有了虛方法,對象實例中就不需要為了支持多態而去包含指向類型信息的指針,故對這個struct取sizeof得到的大小等於各個字段(不包括class字段)sizeof大小之和。
而任何將原始struct對象進行向基類的轉換,都會造成裝箱,裝箱類型就是PeopleBox:
1 People orgin = new People();
2 object _object = orgin; // object _object = new PeopleBox(orgin);
3 ValueType _valueType = orgin; // ValueType _valueType = new PeopleBox(orgin);
4 IFormattable _iformattable = orgin; // IFormattable _iformattable = new PeopleBox(orgin);
所以,在struct中聲明的虛函數和接口繼承,只在裝箱後對象上完全發揮了效果,對於struct對象本身,這些虛方法退化成了靜態調用(編譯期綁定)。
綠色通道:好文要頂關注我收藏該文與我聯系