香蕉视频app

Keep on going never give up.

Let's Go

C# 學習筆記(53)裝箱和拆箱

C#Lonely2019-05-31 20:58:31128次0條

C#中的類型轉換指的是將一種數據類型轉換成另一種數據類型的過程。例如,可以將字符串“123456”轉換成整數類型的123456。但并不是所有的類型都可以進行轉換(例如不能把DateTime對象轉換成int類型),類型之間不能完成轉換時將導致編譯錯誤或運行時錯誤。

類型轉換的方式主要有以下幾種:

隱式類型轉換:由低級別類型向高級別類型的轉換過程。例如,派生類可以隱式地轉換為它的父類。

顯式類型轉換:也稱為強制類型轉換,但是這種轉換可能會導致精度損失或者出現運行時異常。

通過isas香蕉视频app運算符可以進行安全的類型轉換,通過.NET類庫中的Convert類來完成類型轉換。


下面主要介紹值類型與引用類型間的一種轉換——裝箱和拆箱

裝箱指的是將值類型轉換為引用類型的過程,而拆箱指的是將引用類型轉換為值類型。裝箱過程中,系統會在托管堆中生成一份棧中值類型對象的副本;而拆箱則是從托管堆中將引用類型所指向的已裝箱數據復制回值類型對象的過程。

香蕉视频app 為了幫助大家更好的理解裝箱和拆箱的原理,下面通過示例從內存的角度對兩個過程進行深入分析。具體的C#代碼如下所示:

int n = 10;
object o = n; //裝箱,裝箱就屬于隱式類型轉換
int num = (int)o; //拆箱

以上代碼分別執行了一次裝箱和拆箱操作,裝箱操作可以具體分為一下3個步驟:

(1)、內存分配:在托管堆中分配好內存空間以存放復制的實際數據。

香蕉视频app (2)、完成實際數據的復制:將值類型實例的實際數據復制到新分配的內存中。

香蕉视频app (3)、地址返回:將托管堆中的對象地址返回給引用類型變量。

香蕉视频app 裝箱過程就是通過這3步來完成的,下圖更形象地描述了這一過程。

image.png

在IL代碼中,裝箱過程是由box指令來實現的,上一段代碼對應的IL代碼如下所示:

image.png

在這段IL代碼中,除了有box指令外,我們還看到了一個unbox指令,正如其字面意思所提示的一樣,該指令就是完成拆箱操作的IL指令。拆箱過程也可以化分為具體的3個步驟:

(1)、檢查實例:首先檢查要進行拆箱操作的引用類型變量是否為null,如果是為null則拋出NullReferenceException異常;如果不為null則繼續檢查變量是否和拆箱后的類型是同一類型,若結果為否,會導致InvalidCastException異常。

(2)、地址返回:返回已裝箱變量的實際數據部分的地址。

(3)、數據的復制:將托管堆中的實際數據復制到棧中。

拆箱的3個步驟可以更形象地理解為下圖所示的過程:

image.png

對于裝箱與拆箱的理解之所以是如此重要,主要是因為裝箱和拆箱操作對性能有很大的影響。如果程序代碼中存在過多的裝箱和拆箱操作,由于兩個過程都需要進行數據復制,該操作會耗費大量額外運行時間;并且裝箱和拆箱過程中必然會產生多余的對象,這一步加重了GC(垃圾回收器)的負擔,導致程序的性能降低,此外還可能引起一些隱藏的Bug。

代碼示例:

//ArrayList list = new ArrayList(); //00:00:12.5826633
List<int> list = new List<int>();   //00:00:01.0082705
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 100000000; i++)
{
    list.Add(i);
}
sw.Stop();
Console.WriteLine(sw.Elapsed);
Console.ReadKey();

從上代碼可以看出其性能比較的差異,所以我們在寫代碼時,應盡量避免裝箱和拆箱操作,最好用泛型來編程。


總結:

裝箱就是將 值類型 轉換為 引用類型

拆箱就是將 引用類型 轉換為 值類型

看兩種類型是否發生了裝箱或拆箱,要看這兩種類型是否存在繼承關系。


暗錨,解決錨點偏移

文章評論

    嘿,來試試登錄吧!