[C# Tips]再談實值型別的裝箱和拆箱

來源:互聯網
上載者:User

Freesc Huang @ HUST All Rights Reserved
2008-2-11

Keywords
.NET Framework,C#,實值型別,裝箱,拆箱,CLR

本文
半年之前,我曾經寫過一篇關於實值型別裝箱問題的短文(這裡),現在看來,有些東西當時還是沒有完全說開,這次特地拿了一個例子再來談談。理解這些問題,對於一個.NET程式員來說很基礎,也很重要,對我們理解CLR和編寫高效的程式都是很有協助的。至於什麼是實值型別,什麼是裝箱拆箱(box&unbox),在此不做贅述,先來看看下面短短的幾行代碼:

Code
 1    internal struct Ticket
 2    {
 3        private String _start, _terminal;//起點和終點
 4        private Int32 _distance;//距離
 5
 6        public Ticket(string start, string terminal, Int32 distance)
 7        {
 8            _start = start;
 9            _terminal = terminal;
10            _distance = distance;
11        }
12        /**//// <summary>
13        /// 重新訂票
14        /// </summary>
15        /// <param name="newTerminal">新的終點站</param>
16        /// <param name="newDistance">到終點站的距離</param>
17        public void Rebook(String newTerminal, Int32 newDistance)
18        {
19            _terminal = newTerminal;
20            _distance = newDistance;
21        }
22        /**//// <summary>
23        /// 重寫System.ValueType的ToString方法
24        /// </summary>
25        public override String ToString()
26        {
27            return String.Format("From {0} To {1} , {2} km", 
28                _start,
29                _terminal,
30                _distance);//在方法的內部,_distance被裝箱
31        }
32    }
33
34 public sealed class Program
35    {
36      public static void Main()
37        {
38            Ticket t = new Ticket("北京", "漢口", 1225);
39          //實值型別執行個體t在這裡第一次被裝箱:Ticket-->Object-->override ToString
40           Console.WriteLine(t);
41
42          //顯示的裝箱
43          // Console.WriteLine(((Object)t).ToString());
44          
45           t.Rebook("上海", 1400);
46           Console.WriteLine(t);
47
48           Object o = t;
49           Console.WriteLine(o);
50
51           ((Ticket)o).Rebook("廣州",2000);
52           Console.WriteLine(o);
53        }
54    }

程式很短很簡單,定義了一張火車票(Ticket)的結構,它只包括起點,終點和裡程,它是實值型別(結構派生自System.ValueType)。而我們主要關注的是圍繞這張火車票的幾個輸出。

首先,建立了一個火車票的執行個體t(第38行), 初始化為北京到漢口,1225公裡,接著第一次調用Console.WriteLine,因為Console.WriteLine() 沒有參數為Ticket的重載,這裡會對t進行裝箱(就像這裡提到的一樣),而這個“已裝箱”的票(姑且稱作tII)會被CLR預設為是個Object,然後在這個Object執行個體tII上調用ToString(),而此時CLR在這個已裝箱的Ticket的方法表中發現這個類型重寫了ToString()方法,它會隱式的調用這個方法讓我們順利的得到顯示"From 北京 To 漢口,1225 km"。也許我注釋掉的那句代碼會讓您更好的理解這個過程。

隨後,還是在那個實值型別的t(而非tII)上調用Rebook,改成去上海,1400公裡。然後再調用WriteLine,經過與前面相同的過程,我們如願得到了輸出"From 北京 To 上海,1400 km"。

接著,我們顯式地將t裝箱,其實這兩行代碼(第48,49行)跟第43行注釋掉的代碼是一樣的,WriteLine()方法本身就有一個參數為Object的重載。於是我們仍能如願得到"From 北京 To 上海,1400 km"。

接下來這一句比較有趣了,我們將o指向的tII拆箱,將相應的欄位複製到堆棧上一個實值型別執行個體tIII中,然後再對tIII調用Rebook方法,將其改為從北京到廣州,注意,這裡的修改是在堆棧上直接進行的。與託管堆上的o沒有任何關係,於是此時調用ConsoleWriteLine(o);輸出仍然為"From 北京 To 上海,1400 km"。

為了不讓tIII成為游離於我們控制之外的垃圾,我們讓Rebook立刻返回給我們這個tIII,並複製給一個叫t2的執行個體,並輸出,這樣我們才得到了修改過的最終結果"From 北京 To 廣州,2000 km"。
程式輸出如下:

程式碼在這裡
本文算作是對之前那篇隨筆的補充和擴充,也希望通過這個例子能讓大家更加明白實值型別裝箱拆箱的原理和CLR在這背後的 行為,達人們輕點拍。歡迎大家多交流:-)

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.