C#中的實值型別及裝箱[翻譯]

來源:互聯網
上載者:User

我翻譯這篇文章寫得很好,解釋得很清楚,尤其是其圖文,讓人思路清晰,比MS提供的SDK上請的好懂多了,反正我當年看了好幾次還似懂非懂的。我還會翻譯我關於C#中的堆與棧的一個系列文章(共四篇),敬請期待哦。

The C# Value Type and Boxing

Matthew Cochran

儘管在.NET架構裡,我們不需要為記憶體管理以及垃圾收集操心,但我們還是應該瞭解它們,來最佳化我們的應用程式。其中之一便是,公用語言運行環境(CLR)是如何處理對實值型別的引用的。

當一個實值型別執行個體被轉換成System.Object類型或是介面時,CLR需要把實值型別轉換為一個恰當的參考型別。然後在託管堆上分配記憶體並將對象拷上去。我們之所以要瞭解它,有兩個原因:裝箱是一個非常耗費資源的過程(將整個對象從棧上拷到堆上會耗費處理器周期以及託管堆空間),我們因此(裝箱)而有了兩個在記憶體中可以有相矛盾狀態的對象。

這是一個簡單的裝箱的例子。

int i = 0;

object obj = (object) i;     //裝箱
obj = ((int) i) + 3;     // 在堆上改變i的值
i += 1;     // 在棧上改變i的值 

Console.WriteLine(obj.ToString());
Console.WriteLine(i.ToString());

結果:

 

下面來一步一步分析這一過程

第一步:i 被放放置在棧上

 

第二步:i 被拷到堆上(裝箱)並將 obj 放到棧上,obj 的值是在堆上的新對象的內在地址(即指標)

 

第三步:在堆上的對象被更新,值變為3

第四步:在棧上的對象 i 被更新,值變為1

下面是一個更複雜的例子:

class Class1

{

    [STAThread]

    static void Main(string[] args)

    {

        MyInt test = new MyInt();

        test.value = 100;

        test.AddOne();

        aDelegate del = new aDelegate(test.AddOne);

        del();

        test.AddOne();

    }

}

public delegate void aDelegate();

public struct MyInt

{

    public int value;

    public void AddOne()

    {

        Console.WriteLine(string.Format("Before: {0}", value.ToString()));

        value += 1;

        Console.WriteLine(string.Format("After : {0}", value.ToString()));

    }

}

下面是輸出

結果有沒有讓你感意外?發生了什嗎?讓我們來分析一下它的過程。在Main()和args[]被置於棧上後,主函數開始執行:

第一步:test被置於棧上

第二步:AddOne()被置於棧上並執行,將Mint.value的值變為101;

第三步:AddOne()被從棧上移除,Myint被裝箱(拷)到堆上。現在Del指向MyInt對象在堆上的版本。

第四步:裝箱的對象的AddOne()被調用,將其值更新為102.

第五步:來自堆的AddOne()被移除

第六步:Myint對象在棧上的版本(即test)的AddOne()入棧,將其值變為102.

最後:主函數執行完畢,棧被清空。而留在堆上的對象則等待被記憶體回收。

下面讓我們做一個改變,來提高我們的應用程式的效能,並使其結果更符合我們的意願。如果我們將MyInt改為參考型別,我們就只需處理在堆上的一個對象並且沒有裝箱的過程。

public class MyInt

{

    public int value;

    public void AddOne()

    {

        Console.WriteLine(string.Format("Before: {0}", value.ToString()));

        value += 1;

        Console.WriteLine(string.Format("After : {0}", value.ToString()));

    }

}

修改後,我們得到了我們期望的結果:

Before: 100
After : 101
Before: 101
After : 102
Before: 102
After : 103

總結

當我們在一個實值型別上調用ToString()或是GetType(),會有更多的意想不到的裝箱過程。因為在這種情況下,方法是從基類(System.Object)被調用,所以必需經過裝箱過程後,方法才能被調用。當我們將實值型別加入ArrayList()(在這種情況下它們被類型轉換為System.Object)時也會發生裝箱過程。你可以想象,當裝箱過程大規模發生時,將會嚴重影響程式的效能。

希望這篇文章能讓你對實值型別的裝箱過程,以及在通過引用如介面、委託或Arralist來處理實值型別時應注意些什麼有更好的理解。

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.