1. 程式人生 > >C# 值類型和引用類型

C# 值類型和引用類型

微軟官方 賦值 generic 調用方法 線程 朋友 出現 另一個 CI

  有一段講解值類型和引用類型的段落很好。摘抄自一本書內,如下:

  在分析C#中的值類型和引用類型之前,講兩個例子來使抽象的概念變得具體。

  假設你在看一本書,你的朋友此時也想看你手上的那本書。為了讓朋友看到,要麽將自己受傷的書借給對方,要麽再復制一本給他。無論如何做,都是對書本身進行操作,而復制後的兩本書,完全是獨立的,沒有任何的關系。這種行為就可以類比C#中的值類型的行為。

  換另一種假設,假設你正在看電視,換臺、調音量等對電視的操作都通過使用電視遙控器,而不是直接對電視操作。若你的朋友要看別的電視臺或者改變音量,無需把電視給他,只需要把遙控器給他就好了。這就是引用類型的行為。

C#中的大部分是引用類型,但在實際的開發過程中,值類型還是常用的。引用類型總是從托管堆分配,C#要求所有對象都使用new 操作符創建。

區別:最大的區別是,基於值類型的變量直接包含值。也就是說將一個值類型變量賦給另一個值類型變量時,將直接復制其包含的值,引用類型變量的賦值只是復制對象的引用。舉一個栗子,若所有類型都是引用類型的,那麽哪怕是一個數字都是引用類型的,也就是說使用一個數字1都是需要進行一次消耗巨大的內存分配。

  另一個差別是,值類型不能派生其他的類型,所有值類型都是隱式密封的,這麽做的目的是為了防止將值類型用作其他類型的基類型。

  變量的值是如何分配的?通常說,變量的值分配的位置與聲明該變量的位置有關。局部變量的值總是存儲在線程棧上,實例變量的值和實例本身存儲在實例存儲的地方。引用類型實例和靜態變量總是存儲在堆上。

  值類型的實例一般是分配在線程棧上而不是全部,是由於有些情況下,值類型也有可能分配在托管堆上。

這些特殊情況包括數組中的元素,引用類型中的值類型字段,叠代器塊中的局部變量,閉包情況下匿名函數(Lamda)中的局部變量。此時,有可能出現的情況是,若該情況下的值類型實例如分配在線程棧上,有可能會出現線程棧中的方法已經調用結束,但還是會訪問這些值的情況,值會隨著調用方法的返回而被清除掉。因此它們被分配在托管堆上,以滿足在方法返回之後還能被訪問的要求。

  因此,單純的說“引用類型保存在托管堆上,值類型保存在線程棧上”是不準確的。準確來說,引用類型總是分配在托管堆上,但值類型並非總是分配在線程棧上。

  任何被稱為“類”的類型都是引用類型。特別註意的是,字符串string並非是一個實際的字符串,而是對字符串的一個引用,故而字符串是引用類型。

  C#中常見的引用類型,如System.Collections.Generic.List類、System.Text.Decoder類。

  與引用類型相對的就是值類型,可分為結構和枚舉兩種。

  結構又分為三種:

  • 數字型結構:System.Int32結構、System.Float結構、System.Decimal結構等。
  • 布爾型結構:常見即System.Boolean等。
  • 用戶自定義的結構。

根據微軟官方的語言規範文檔,所有的值類型都必須派生自System.VlaueType。另,值類型有兩種表示方式,分別是未裝箱和已裝箱。而裝箱機制就是?將值類型轉換成引用類型。是由於值類型不在托管堆中分配,不被垃圾回收。但很多情況下,需要獲取和操作對值類型實例的引用,此時裝箱機制應運而生。

C# 值類型和引用類型