程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#中值類型和援用類型的差別深度剖析

C#中值類型和援用類型的差別深度剖析

編輯:C#入門知識

C#中值類型和援用類型的差別深度剖析。本站提示廣大學習愛好者:(C#中值類型和援用類型的差別深度剖析)文章只能為提供參考,不一定能成為您想要的結果。以下是C#中值類型和援用類型的差別深度剖析正文


本文淺顯易懂的剖析了C#中值類型和援用類型的差別。分享給年夜家供年夜家參考。詳細剖析以下:

仿佛“值類型和援用類型的差別”是本年面試的風行趨向,我已然是持續三次(今朝總共也就三次)面試第一個成績就碰到這個了,這是多年夜的幾率啊,100%,哈哈,我該買彩票去!

言歸正傳,咱照樣先來商量商量這兩者之間有甚麼差別吧。記得有一次德律風面試中,我直接跟面試官說:“值類型是現金,援用類型是存折”,後來想一想其時說這話雖是有點兒激動地信口開河,但也沒甚麼不當。我此人不擅長悖理論的教條,愛好把書本上那些僵硬的話跟實際生涯中罕見的事物接洽起來懂得和記憶。

直白點兒說:值類型就是現金,要用直接用;援用類型是存折,要用還得先去銀行取現。

聲明一個值類型變量,編譯器會在棧上分派一個空間,這個空間對應著該值類型變量,空間裡存儲的就是該變量的值。援用類型的實例分派在堆上,新建一個援用類型實例,獲得的變量值對應的是該實例的內存分派地址,這就像您的銀行賬號一樣。詳細哪些類型是值類型哪些是援用類型,年夜家翻翻書,背一背就行了,不外我想,做過一段時光的開辟,即便您背不了書上教條的界說,也不會把值類型和援用類型弄混的。接上去,照樣老例子,咱看碼措辭吧。
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
 
public static class ReferenceAndValue
{
    public static void Demonstration()
    {
        Person zerocool = new Person { Name = "ZeroCool", Age = 25 };
        Person anders = new Person { Name = "Anders", Age = 47 };
 
        int age = zerocool.Age;
        zerocool.Age = 22;
 
        Person guru = anders;
        anders.Name = "Anders  Hejlsberg";
 
        Console.WriteLine("zerocool's age:\t{0}", zerocool.Age);
        Console.WriteLine("age's value:\t{0}", age);
        Console.WriteLine("anders' name:\t{0}", anders.Name);
        Console.WriteLine("guru' name:\t{0}", guru.Name);
    }
}
下面這段代碼,我們起首創立了一個Person類,包括了Name和Age兩個屬性,無須置疑,Person類是援用類型,Name也是,由於它是string類型的(但string是很特別的援用類型,前面將專門有一篇文章來評論辯論),但Age則是值類型。接上去我們來看看Demonstration辦法,個中演示的就是值類型跟援用類型的差別。

起首,我們聲清楚明了兩個Person類的實例對象,zerocool和anders,後面提到過,這兩個對象都被分派在堆上,而zerocool和anders自己其實只是對象地點內存區域的肇端地址援用,換句話說就是指向這裡的指針。我們聲明對象實例時也趁便分離停止了初始化,起首我們看,zerocool對象的值類型成員,我們賦值為25(對,我本年25歲),anders(待會兒你們就曉得是誰了)的Name屬性,我們賦值為“Anders”。齊活兒,接上去看我們怎樣干吧。

我們聲明一個值類型變量age,直接在初始化時把zerocool的Age值賦給它,明顯,age的值就是25了。但這個時刻zerocool不愉快了,他想裝嫩,擅自把本身的年紀改成22歲,剛夠法定娶親年紀。然後我們又聲清楚明了一個援用類型的guy對象,初始化時就把anders賦給它,然後anders顯露廬山真面貌了,他的名字叫“Anders Hejlsberg”(在此向C#之父致敬)。接上去我們來分離准許出這幾個變量的值,看看有甚麼差異。

Result01

你能夠要認為奇異(你要不認為奇異,也就不消再接著往下看了),為何我們改了zerocool.Age的值,age沒隨著變,改了anders.Name的值,guru.Name卻隨著變了呢?這就是值類型和援用類型的差別。我們聲明age值類型變量,並將zerocool.Age賦給它,編譯器在棧上分派了一塊空間,然後把zerocool.Age的值填出來,僅此罷了,兩者並沒有任何連累,就像復印機一樣,只是把zerocool.Age的值拷貝給age了。而援用類型紛歧樣,我們在聲明guy的時刻把anders賦給它,後面說過,援用類型包括的是只想堆上數據區域地址的援用,其實就是把anders的援用也賦給guy了,是以這兩者從此指向了統一塊內存區域,既然是指向統一塊區域,那末甭管誰動了外面的“奶酪”,另外一個變現出來的成果也會隨著變,就像信譽卡跟親情卡一樣,用親情卡取了錢,與之聯系關系的信譽卡賬上也會隨著產生變更。一提到錢,估量年夜家伙兒印象就深了些吧,呵呵!

別的,機能上也會有差別的。既然一個是直接操作內存,另外一個則多一步先解析援用地址,那末明顯許多時刻值類型會減小體系機能開支。但“許多時刻”不代表“一切時刻”,有些時刻還得量力而為,例如須要年夜量停止函數參數傳遞或前往的時刻,總是如許停止字段拷貝,其實反而會下降運用法式機能。別的,假如實例會被頻仍地用於Hashtable或許ArrayList之類的聚集中,這些類會對個中的值類型變量停止裝箱操作,這也會招致額定的內存分派和內存拷貝操作,從運用法式機能方面來看,其實也不劃算。

哦對了,下面提到了一個概念,裝箱。那末甚麼是裝箱呢?其實裝箱就是值類型到援用類型的轉化進程。將一個值類型變量裝箱成一個援用類型變量,起首會在托管堆上為新的援用類型變量分派內存空間,然後將值類型變量拷貝到托管堆上新分派的對象內存中,最初前往新分派的對象內存地址。裝箱操作是可逆的,所以還有拆箱操作。拆箱操作獲得指向對象中包括值類型部門的指針,然後由法式員手動將其對應的值拷貝給值類型變量。接上去我們來看看典范的裝箱和拆箱操作。
public static class BoxingAndUnboxing
{
    public static void Demonstration()
    {
        int ageInt = new int();
 
        // Boxing operation.
        object ageObject = ageInt;
 
        //ageObject = null;
 
        // Unboxing operation.
        ageInt = (int)ageObject;
 
        Console.WriteLine(ageInt);
    }
}
在該辦法中,我們起首聲清楚明了一個值類型變量ageInt,但並未給它賦值,接著聲清楚明了一個典范的援用類型變量ageObject,並把ageInt賦給它,這裡就停止了一次裝箱操作。編譯器如今托管堆上分派一塊內存空間(空間年夜小為對象中包括的值類型變量所占空間總和外加一個辦法表指針和一個SyncBlockIndex),然後把ageInt拷貝到這個空間中,再前往該空間的援用地址。接上去第13行則是拆箱操作,編譯器獲得到ageObject對象中值類型變量的指針,然後將其值拷貝給值類型變量。假如你把第10行正文失落的代碼翻開(這是淺顯說法,其實就是撤消正文),那末第13行就會拋出System.NullReferenceException異常,要說問甚麼,這又會牽扯出值類型跟援用類型另外一個年夜的分歧。看見了吧,聲明ageInt時並沒有賦值,假如關失落第10行代碼,法式不會報錯,最初打印出個0,這解釋在聲明值類型變量時,假如沒有初始化賦值,編譯器會主動將其賦值為0,既然值類型沒有援用,那末它就弗成能為空。援用類型紛歧樣,它可認為空援用,一張過時作廢的銀行卡是可以存在。而假如將一個空的對象拆箱,編譯器上哪兒去找它外面的值類型變量的指針呢?所以這也是拆箱操作須要留意的處所。

最初,我們在把值類型和援用類型之間其它一些顯著差別年夜致枚舉以下,以便年夜家能順遂經由過程面試第一問。

一切值類型都繼續自System.ValueType,然則ValueType沒有附加System.Object包括以外其它任何辦法,不外它卻是改寫了Equals和GetHashCode兩個辦法。援用類型變量的Equals比擬的是兩者的援用地址而不是外部的值,值類型變量的Equals辦法比擬的是兩者的值而不是……哦對了,值類型壓根兒沒有援用地址;
值類型不克不及作為其它任何類型的基類型,是以不克不及向值類型中增長任何新的虛辦法,更不應有任何籠統辦法,一切的辦法都是sealed的(弗成重寫);
未裝箱的值類型分派在棧上而不是堆上,而棧又不是GC的地皮兒,是以GC基本不外問值類型變量的逝世活,一旦值類型變量的感化規模一過,它所占的內存空間就立刻被收受接管失落,不勞GC親身著手。
以上枚舉的都是值類型和援用類型之間的重要差別,文碼並茂,信任應當給你留下比擬深入的印象,雖不敷深,希望能起到拋磚引玉的感化。假如去面SDE職位,估量這深度就差不多了。

願望本文所述對年夜家的C#法式設計有所贊助。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved