C# 值類型和引用類型

《CLR via C#》作者Jeffrey Richter的話來說片仿,“不理解【引用類型】和【值類型】區(qū)別的程序員將會給代碼引入詭異的bug和性能問題(I believe that a developer who misunderstands the difference between reference types and value types will introduce subtle bugs and performance issues into their code.)”彬坏。這就要求我們正確理解和使用值類型和引用類型嚼松。

C#中數(shù)據(jù)類型為CTS(Common Type System)岛啸,包括值類型和引用類型椅棺;值類型包含【布爾類型】攘残、【字符型】和【枚舉類型】讨惩。

枚舉類型(enum)是由一組特定常量構成的一組數(shù)據(jù)結構,是值類型的一種特殊形式氏身,它從System.Enum繼承而來巍棱,并為基礎類型的值提供替代名詞。枚舉類型有名稱蛋欣、基礎類型和一組字段航徙。基礎類型必須是內置的整數(shù)類型陷虎,字段是靜態(tài)文本字段到踏,其中的每一個字段都表示常數(shù),同一個值可以分配給多個字段尚猿,出現(xiàn)這種情況時窝稿,必須將其中某個值標記為主要枚舉值,以便進行反射和字符串轉換凿掂。枚舉聲明可以顯式地聲明 byte伴榔、sbyte纹蝴、short、ushort踪少、int塘安、uint、long 或 ulong 類型作為對應的基礎類型援奢,沒有顯式地聲明基礎類型的枚舉聲明意味著所對應的基礎類型是int兼犯。

用戶可以將基礎類型的值分配給枚舉,反之亦然(運行庫不要求強制轉換)集漾;也可創(chuàng)建枚舉的實例切黔,并調用System.Enum的方法以及對枚舉基礎類型定義的任何方法。對于枚舉還有以下附加限制:

1.枚舉不能定義自己的方法

2.枚舉不能實現(xiàn)接口

3.枚舉不能定義屬性或事件

注意點:

1.對于沒有賦值的枚舉類型具篇,聲明的第一個枚舉成員它的默值為0绕娘,以后的枚舉成員值是將前一個枚舉成員(按照文本順序)的值加1得到的。

2.允許多個枚舉成員有相同的值栽连。沒有顯示賦值的枚舉成員的值,總是前一個枚舉成員的值加1侨舆。

3.使用時注意類型轉換秒紧。

char代表無符號的16位整數(shù),數(shù)值范圍從0~65535挨下。 char類型的可能值對應于統(tǒng)一字符編碼標準(Unicode)的字符集熔恢。char類型與其他整數(shù)類型相比有以下兩點不同之處:

1.沒有其他類型到char類型的隱式轉換。即使是對于sbyte臭笆,byte和ushort這樣能完全使用char類型代表其值的類型叙淌, sbyte,byte和ushort到char的隱式轉換也不存在愁铺。

2.char類型的常量必須被寫為字符形式鹰霍,如果用整數(shù)形式,則必須帶有類型轉換前綴茵乱。

比如(char)10賦值形式有三種: char chsomechar="A"; char chsomechar="\x0065"; 十六進制 char chsomechar="\u0065 ;

unicode表示法字符型中有下列轉義符:

\'用來表示單引號

\"用來表示雙引號

\\ 用來表示反斜杠

\0 表示空字符

\a 用來表示感嘆號

\b 用來表示退格

\f 用來表示換頁

\n 用來表示換行

\r 用來表示回車

\t 用來表示水平tab

\v 用來表示垂直tab

類型說明及范圍

float類型精確到小數(shù)點后面7位茂洒。double類型精確到小數(shù)點后面15位或16位株搔。decimal關鍵字表示128位數(shù)據(jù)類型车酣。同浮點型相比,decimal類型具有更高的精度和更小的范圍扳抽,這使它適合于財務和貨幣計算斤贰,精確到小數(shù)點后面28到29位智哀。


1. 通用類型系統(tǒng)

C#中,變量是值還是引用僅取決于其數(shù)據(jù)類型荧恍。C#的基本數(shù)據(jù)類型都以平臺無關的方式來定義瓷叫。C#的預定義類型并沒有內置于語言中,而是內置于.NET Framework中。.NET使用通用類型系統(tǒng)(CTS)定義了可以在中間語言(IL)中使用的預定義數(shù)據(jù)類型赞辩,所有面向.NET的語言都最終被編譯為 IL雌芽,即編譯為基于CTS類型的代碼。

例如辨嗽,在C#中聲明一個int變量時世落,聲明的實際上是CTS中System.Int32的一個實例。這具有重要的意義:

確保IL上的強制類型安全糟需;

實現(xiàn)了不同.NET語言的互操作性屉佳;

所有的數(shù)據(jù)類型都是對象。它們可以有方法洲押,屬性武花,等。例如:

int i;

i = 1;

string s;

s = i.ToString();

MSDN的這張圖說明了CTS中各個類型是如何相關的杈帐。注意体箕,類型的實例可以只是值類型或自描述類型,即使這些類型有子類別也是如此挑童。

2. 值類型

C#的所有值類型均隱式派生自System.ValueType: 結構體:struct(直接派生于System.ValueType)累铅;

數(shù)值類型:

整 型:sbyte(System.SByte的別名),short(System.Int16)站叼,int(System.Int32)娃兽,long (System.Int64),byte(System.Byte)尽楔,ushort(System.UInt16)投储,uint (System.UInt32),ulong(System.UInt64)阔馋,char(System.Char)玛荞;

浮點型:float(System.Single),double(System.Double)垦缅;

用于財務計算的高精度decimal型:decimal(System.Decimal)冲泥。

bool型:bool(System.Boolean的別名);

用戶定義的結構體(派生于System.ValueType)壁涎。

枚舉:enum(派生于System.Enum)凡恍;

可空類型(派生于System.Nullable泛型結構體,T?實際上是System.Nullable的別名)怔球。

每種值類型均有一個隱式的默認構造函數(shù)來初始化該類型的默認值嚼酝。例如:

int i = new int();

等價于:

Int32 i = new Int32();

等價于:

int i = 0;

等價于:

Int32 i = 0;

使用new運算符時,將調用特定類型的默認構造函數(shù)并對變量賦以默認值竟坛。在上例中闽巩,默認構造函數(shù)將值0賦給了i钧舌。MSDN上有完整的默認值表。

所有的值類型都是密封(seal)的涎跨,所以無法派生出新的值類型洼冻。

值得注意的是,System.ValueType直接派生于System.Object隅很。即System.ValueType本身是一個類類型撞牢,而不是值類型。其關鍵在于ValueType重寫了Equals()方法叔营,從而對值類型按照實例的值來比較屋彪,而不是引用地址來比較。

可以用Type.IsValueType屬性來判斷一個類型是否為值類型:

復制代碼 代碼如下:

TestType testType = new TestType ();

if (testType.GetType().IsValueType)

{

Console.WriteLine("{0} is value type.", testType.ToString());

}

3. 引用類型

C#有以下一些引用類型:

數(shù)組(派生于System.Array)

用戶定義的以下類型:

類:class(派生于System.Object)绒尊;

接口:interface(接口不是一個“東西”畜挥,所以不存在派生于何處的問題。Anders在《C# Programming Language》中說婴谱,接口只是表示一種約定[contract])蟹但;

委托:delegate(派生于System.Delegate);

object:(System.Object的別名)谭羔;

字符串:string(System.String的別名)矮湘。

可以看出:

引用類型與值類型相同的是,結構體也可以實現(xiàn)接口口糕;

引用類型可以派生出新的類型,而值類型不能磕蛇;

引用類型可以包含null值景描,值類型不能(可空類型功能允許將 null 賦給值類型);

引用類型變量的賦值只復制對對象的引用秀撇,而不復制對象本身超棺。而將一個值類型變量賦給另一個值類型變量時,將復制包含的值呵燕。

對于最后一條棠绘,經(jīng)常混淆的是string再扭。我曾經(jīng)在一本書的一個早期版本上看到String變量比string變量效率高氧苍;我還經(jīng)常聽說String是引用類型,string是值類型泛范,等等让虐。例如:

string s1 = "Hello, ";

string s2 = "world!";

string s3 = s1 + s2; ? //s3 is "Hello, world!"

這確實看起來像一個值類型的賦值。再如:

string s1 = "a";

string s2 = s1;

s1 = "b"; ? //s2 is still "a"

改變s1的值對s2沒有影響罢荡。這更使string看起來像值類型赡突。實際上对扶,這是運算符重載的結果,當s1被改變時惭缰,.NET在托管堆上為s1重新分配了內存浪南。這樣的目的,是為了將做為引用類型的string實現(xiàn)為通常語義下的字符串漱受。

4. 值類型和引用類型在內存中的部署

經(jīng)常聽說络凿,并且經(jīng)常在書上看到:值類型部署在棧上,引用類型部署在托管堆上拜效。實際上并沒有這么簡單喷众。

MSDN上說:托管堆上部署了所有引用類型。這很容易理解紧憾。當創(chuàng)建一個應用類型變量時:

object reference = new object();

關鍵字new將在托管堆上分配內存空間到千,并返回一個該內存空間的地址。左邊的reference位于棧上赴穗,是一個引用憔四,存儲著一個內存地址;而這個地址指向的內存(位于托管堆)里存儲著其內容(一個System.Object的實例)般眉。下面為了方便了赵,簡稱引用類型部署在托管推上。

再來看值類型甸赃∈裂矗《C#語言規(guī)范》 上的措辭是“結構體不要求在堆上分配內存(However, unlike classes, structs are value types and do not require heap allocation)”而不是“結構體在棧上分配內存”。這不免容易讓人感到困惑:值類型究竟部署在什么地方埠对?

5. 正確使用值類型和引用類型

這一部分主要參考《Effective C#》络断,希望能讓你加深對值類型和引用類型的理解。

C#中项玛,變量是值還是引用僅取決于其數(shù)據(jù)類型貌笨。

C#的值類型包括:結構體(數(shù)值類型,bool型襟沮,用戶定義的結構體)锥惋,枚舉,可空類型开伏。

C#的引用類型包括:數(shù)組膀跌,用戶定義的類、接口固灵、委托淹父,object,字符串怎虫。

數(shù)組的元素暑认,不管是引用類型還是值類型困介,都存儲在托管堆上。

引用類型在棧中存儲一個引用蘸际,其實際的存儲位置位于托管堆座哩。為了方便,本文簡稱引用類型部署在托管推上粮彤。

值類型總是分配在它聲明的地方:作為字段時根穷,跟隨其所屬的變量(實例)存儲;作為局部變量時导坟,存儲在棧上屿良。

值類型在內存管理方面具有更好的效率,并且不支持多態(tài)惫周,適合用作存儲數(shù)據(jù)的載體尘惧;引用類型支持多態(tài),適合用于定義應用程序的行為递递。

應該盡可能地將值類型實現(xiàn)為具有【常量性】和【原子性】的類型喷橙。

應該盡可能地確保0為值類型的有效狀態(tài)。

應該盡可能地減少裝箱和拆箱登舞。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末贰逾,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子菠秒,更是在濱河造成了極大的恐慌疙剑,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件践叠,死亡現(xiàn)場離奇詭異核芽,居然都是意外死亡,警方通過查閱死者的電腦和手機酵熙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驰坊,“玉大人匾二,你說我怎么就攤上這事∪剑” “怎么了察藐?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長舟扎。 經(jīng)常有香客問我分飞,道長,這世上最難降的妖魔是什么睹限? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任譬猫,我火速辦了婚禮讯檐,結果婚禮上,老公的妹妹穿的比我還像新娘染服。我一直安慰自己别洪,他們只是感情好,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布柳刮。 她就那樣靜靜地躺著挖垛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪秉颗。 梳的紋絲不亂的頭發(fā)上痢毒,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音蚕甥,去河邊找鬼哪替。 笑死,一個胖子當著我的面吹牛梢灭,可吹牛的內容都是我干的夷家。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼敏释,長吁一口氣:“原來是場噩夢啊……” “哼库快!你這毒婦竟也來了?” 一聲冷哼從身側響起钥顽,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤义屏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蜂大,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闽铐,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年奶浦,在試婚紗的時候發(fā)現(xiàn)自己被綠了兄墅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡澳叉,死狀恐怖隙咸,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情成洗,我是刑警寧澤五督,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站瓶殃,受9級特大地震影響充包,放射性物質發(fā)生泄漏。R本人自食惡果不足惜遥椿,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一基矮、第九天 我趴在偏房一處隱蔽的房頂上張望淆储。 院中可真熱鬧,春花似錦愈捅、人聲如沸遏考。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽灌具。三九已至,卻和暖如春譬巫,著一層夾襖步出監(jiān)牢的瞬間咖楣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工芦昔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留诱贿,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓咕缎,卻偏偏與公主長得像珠十,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子凭豪,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內容