|
第四章 C#類(lèi)型
既然你知道了怎樣創(chuàng)建一個(gè)簡(jiǎn)單的C#程序,我將會(huì)給你介紹C#的類(lèi)型系統(tǒng)。在這一章中,你學(xué)到如何使用不同的值和 引用類(lèi)型,加框和消框機(jī)制能為你作些什么。盡管這一章的不側(cè)重于例子,但你可以學(xué)到很多重要的信息,關(guān)于如何創(chuàng)建 現(xiàn)成類(lèi)型的程序。 4.1 值類(lèi)型 各種值類(lèi)型總是含有相應(yīng)該類(lèi)型的一個(gè)值。C#迫使你初始化變量才能使用它們進(jìn)行計(jì)算-變量沒(méi)有初始化不會(huì)出問(wèn)題, 因?yàn)楫?dāng)你企圖使用它們時(shí),編譯器會(huì)告訴你。 每當(dāng)把一個(gè)值賦給一個(gè)值類(lèi)型時(shí),該值實(shí)際上被拷貝了。相比,對(duì)于引 用類(lèi)型,僅是引用被拷貝了,而實(shí)際的值仍然保留在相同的內(nèi)存位置,但現(xiàn)在有兩個(gè)對(duì)象指向了它(引用它)。C#的值類(lèi) 型可以歸類(lèi)如下: ·簡(jiǎn)單類(lèi)型(Simple types ) ·結(jié)構(gòu)類(lèi)型(struct types) ·枚舉類(lèi)型(Enumeration types) 4.1.1 簡(jiǎn)單類(lèi)型 在C#中出現(xiàn)的簡(jiǎn)單類(lèi)型共享一些特性。第一,它們都是.NET系統(tǒng)類(lèi)型的別名。第二,由簡(jiǎn)單類(lèi)型組成的常量表達(dá)式僅 在編譯時(shí)而不是運(yùn)行時(shí)受檢測(cè)。最后,簡(jiǎn)單類(lèi)型可以按字面被初始化。以下為C#簡(jiǎn)單類(lèi)型歸類(lèi): ·整型 ·布爾型 · 字符型 (整型的一種特殊情況) ·浮點(diǎn)型 ·小數(shù)型
4.1.1.1 整型 C#中有9個(gè)整型。 sbyte 、byte、 short、 ushort、 int、 uint、 long、 ulong 和 char(單獨(dú)一節(jié)討論)。它們 具有以下特性:
·sbyte型為有符號(hào)8位整數(shù),取值范圍在128~127之間。 ·bytet型為無(wú)符號(hào)16位整數(shù),取值范圍在0~255之間。 ·short型為有符號(hào)16位整數(shù),取值范圍在-32,768~32,767之間。 ·ushort型為無(wú)符號(hào)16位整數(shù),取值范圍在0~65,535之間。 ·int型為有符號(hào)32位整數(shù),取值范圍在-2,147,483,648~ 2,147,483,647之間。 ·uint型為無(wú)符號(hào)32位整數(shù),取值范圍在 0 ~ 4,294,967,295之間。 ·long型為64位有符號(hào)整數(shù),取值范圍在9,223,372,036,854,775,808~ 9,223,372,036,854,775,807之間。 ·ulong型為64位無(wú)符號(hào)整數(shù),取值范圍在0 ~ 18,446,744,073,709,551,615之間。
VB和C程序員都可能會(huì)對(duì)int和long數(shù)據(jù)類(lèi)型所代表的新范圍感到驚訝。和其它的編程語(yǔ)言相比,在C#中,int不再取決 于一個(gè)機(jī)器的字(word)的大小,而long被設(shè)成64位。
4.1.1.2 布爾型 布爾數(shù)據(jù)類(lèi)型有true和false兩個(gè)布爾值。可以賦于true或false值給一個(gè)布爾變量,或可以賦于一個(gè)表達(dá)式,其所求 出的值等于兩者之一: bool bTest = (80 > 90); 與C和C++相比,在C#中,true值不再為任何非零值。不要為了增加方便而把其它整型轉(zhuǎn)換成布爾型。
4.1.1.3 字符型 字符型為一個(gè)單Unicode 字符。一個(gè)Unicode字符16位長(zhǎng),它可以用來(lái)表示世界上多種語(yǔ)言?梢园匆韵路椒ńo一個(gè)字 符變量賦值: char chSomeChar = 'A'; 除此之外,可以通過(guò)十六進(jìn)制轉(zhuǎn)義符(前綴\x)或Unicode表示法給變量賦值(前綴\u): char chSomeChar = '\x0065'; char chSomeChar = '\u0065'; 不存在把char轉(zhuǎn)換成其它數(shù)據(jù)類(lèi)型的隱式轉(zhuǎn)換。這就意味著,在C#中把一個(gè)字符變量當(dāng)作另外的整數(shù)數(shù)據(jù)類(lèi)型看待是 行不通的——這是C程序員必須改變習(xí)慣的另一個(gè)方面。但是,可以運(yùn)用顯式轉(zhuǎn)換: char chSomeChar = (char)65; int nSomeInt = (int)'A'; 在C中仍然存在著轉(zhuǎn)義符(字符含義)。要換換腦筋,請(qǐng)看表4.1。
Table 4.1 轉(zhuǎn)義符( Escape Sequences)
轉(zhuǎn)義符 字符名 \' 單引號(hào) \" 雙引號(hào) \\ 反斜杠 \0 空字符 \a 感嘆號(hào)(Alert ) \b 退格 \f 換頁(yè) \n 新行 \r 回車(chē) \t 水平 tab \v 垂直tab
4.1.1.4 浮點(diǎn)型 兩種數(shù)據(jù)類(lèi)型被當(dāng)作浮點(diǎn)型:float和double。它們的差別在于取值范圍和精度: float: 取值范圍在 1.5x10^-45~ 3.4x10^38之間, 精度為7位數(shù)。 double: 取值范圍在 5.0x10^-324 ~ 1.7x10^308之間, 精度為 15~16 位數(shù)。 當(dāng)用兩種浮點(diǎn)型執(zhí)行運(yùn)算時(shí),可以產(chǎn)生以下的值: 正零和負(fù)零 正無(wú)窮和負(fù)無(wú)窮 非數(shù)字值(Not-a-Number,縮寫(xiě)NaN) 非零值的有限數(shù)集 另一個(gè)運(yùn)算規(guī)則為,當(dāng)表達(dá)式中的一個(gè)值是浮點(diǎn)型時(shí),所有其它的類(lèi)型都要被轉(zhuǎn)換成浮點(diǎn)型才能執(zhí)行運(yùn)算。
4.1.1.5 小數(shù)型(The decimal Type) 小數(shù)型是一種高精度、128位數(shù)據(jù)類(lèi)型,它打算用于金融和貨幣的計(jì)算。它所表示的范圍從大約1.0x10^-28 到 7.9x10^28,具有28至29位有效數(shù)字。要注意,精度是以位數(shù) (digits)而不是以小數(shù)位(decimal places)表示。運(yùn)算準(zhǔn)確 到28個(gè)小數(shù)位的最大值。 正如你所看到的,它的取值范圍比double的還窄,但它更精確。因此,沒(méi)有decimal和double之間的隱式轉(zhuǎn)換——往一 個(gè)方向轉(zhuǎn)換可能會(huì)溢出,往另外一個(gè)方向可能會(huì)丟失精度。你不得不運(yùn)用顯式轉(zhuǎn)換。 當(dāng)定義一個(gè)變量并賦值給它時(shí),使用 m 后綴以表明它是一個(gè)小數(shù)型: decimal decMyValue = 1.0m; 如果省略了m,在變量被賦值之前,它將被編譯器認(rèn)作double型。
4.1.2 結(jié)構(gòu)類(lèi)型 一個(gè)結(jié)構(gòu)類(lèi)型可以聲明構(gòu)造函數(shù)、常數(shù)、字段、方法、屬性、索引、操作符和嵌套類(lèi)型。盡管列出來(lái)的功能看起來(lái)象 一個(gè)成熟的類(lèi),但在C#中,結(jié)構(gòu)和類(lèi)的區(qū)別在于結(jié)構(gòu)是一個(gè)值類(lèi)型,而類(lèi)是一個(gè)引用類(lèi)型。與C++相比,這里可以用結(jié)構(gòu)關(guān) 鍵字定義一個(gè)類(lèi)。 使用結(jié)構(gòu)的主要思想是用于創(chuàng)建小型的對(duì)象,如Point和FileInfo等等。你可以節(jié)省內(nèi)存,因?yàn)闆](méi)有如類(lèi)對(duì)象所需的那 樣有額外的引用產(chǎn)生。例如,當(dāng)聲明含有成千上萬(wàn)個(gè)對(duì)象的數(shù)組時(shí),這會(huì)引起極大的差異。 清單4.1 包含一個(gè)命名為IP的簡(jiǎn)單結(jié)構(gòu),它表示一個(gè)使用byte類(lèi)型的4個(gè)字段的IP地址。我不包括方法等,因?yàn)檫@些 工作正如使用類(lèi)一樣,將在下一章有詳細(xì)的描述。
清單4.1 定義一個(gè)簡(jiǎn)單的結(jié)構(gòu)
1: using System; 2: 3: struct IP 4: { 5: public byte b1,b2,b3,b4; 6: } 7: 8: class Test 9: { 10: public static void Main() 11: { 12: IP myIP; 13: myIP.b1 = 192; 14: myIP.b2 = 168; 15: myIP.b3 = 1; 16: myIP.b4 = 101; 17: Console.Write("{0}.{1}.",myIP.b1,myIP.b2); 18: Console.Write("{0}.{1}",myIP.b3,myIP.b4); 19: } 20: }
4.1.3 枚舉類(lèi)型 當(dāng)你想聲明一個(gè)由一指定常量集合組成的獨(dú)特類(lèi)型時(shí),枚舉類(lèi)型正是你要尋覓的。最簡(jiǎn)單的形式,它看起來(lái)可能象這 樣: enum MonthNames { January, February, March, April }; 因我慣用缺省設(shè)置,故枚舉元素是int型,且第一個(gè)元素為0值。每一個(gè)連續(xù)的元素按1遞增。如果你想給第一個(gè)元素直 接賦值,可以如下把它設(shè)成1: enum MonthNames { January=1, February, March, April }; 如果你想賦任意值給每個(gè)元素——甚至相同的值——這也沒(méi)有問(wèn)題: enum MonthNames { January=31, February=28, March=31, April=30 }; 最后的選擇是不同于int的數(shù)據(jù)類(lèi)型?梢栽谝粭l語(yǔ)句中按如此賦值: enum MonthNames : byte { January=31, February=28, March=31, April=30 }; 你可以使用的類(lèi)型僅限于long、int、short和byte。
4.2 引用類(lèi)型 和值類(lèi)型相比,引用類(lèi)型不存儲(chǔ)它們所代表的實(shí)際數(shù)據(jù),但它們存儲(chǔ)實(shí)際數(shù)據(jù)的引用。在C#中提供以下引用類(lèi)型給你 使用: ·對(duì)象類(lèi)型 ·類(lèi)類(lèi) 型 ·接口 ·代表元 ·字符串類(lèi)型 ·數(shù)組
4.2.1 對(duì)象類(lèi)型 對(duì)象類(lèi)型是所有類(lèi)型之母——它是其它類(lèi)型最根本的基類(lèi)。因?yàn)樗撬袑?duì)象的基類(lèi),所以可把任何類(lèi)型的值賦給 它。例如,一個(gè)整型: object theObj = 123; 給所有的C++程序員一個(gè)警告:object并不等價(jià)于你可能正在尋找的void*。無(wú)論如何,忘掉指針總是個(gè)好主意。 當(dāng)一個(gè)值類(lèi)型被加框(作為一個(gè)對(duì)象利用)時(shí),對(duì)象類(lèi)型就被使用了。這一章稍后會(huì)討論到加框和消框
4.2.2 類(lèi)類(lèi)型 一個(gè)類(lèi)類(lèi)型可以包含數(shù)據(jù)成員、函數(shù)成員和嵌套類(lèi)型。數(shù)據(jù)成員是常量、字段和事件。函數(shù)成員包括方法、屬性、索 引、操作符、構(gòu)造函數(shù)和析構(gòu)函數(shù)。類(lèi)和結(jié)構(gòu)的功能是非常相似的,但正如前面所述,結(jié)構(gòu)是值類(lèi)型而類(lèi)是引用類(lèi)型。 和C++相比,僅允許單繼承。(你不能擁有派生一個(gè)新對(duì)象的多重基類(lèi)。) 但是,C#中的一個(gè)類(lèi)可以派生自多重接 口,該接口在下一節(jié)將得到描述。 第五章 “類(lèi)”專(zhuān)門(mén)討論使用類(lèi)編程。這一節(jié)僅打算給出C#類(lèi)在哪里適合類(lèi)型圖的一個(gè)全貌。
4.2.3 接口 一個(gè)接口聲明一個(gè)只有抽象成員的引用類(lèi)型。跟C++中相似的概念為:一個(gè)結(jié)構(gòu)的成員,且方法等于0。如果你不知道 那些概念的任何東西,這里就是在C#中一個(gè)接口實(shí)際所做的。僅僅只存在著方法標(biāo)志,但根本就沒(méi)有執(zhí)行代碼。這就暗示 了不能實(shí)例化一個(gè)接口,只能實(shí)例化一個(gè)派生自該接口的對(duì)象。 可以在一個(gè)接口中定義方法、屬性和索引。所以,對(duì)比一個(gè)類(lèi),接口有什么特殊性呢?當(dāng)定義一個(gè)類(lèi)時(shí),可以派生自 多重接口,而你只能可以從僅有的一個(gè)類(lèi)派生。 你可能會(huì)問(wèn):"OK,但我必須實(shí)現(xiàn)所有的接口成員,那么我能從這個(gè)途徑得到什么呢?" 我想舉一個(gè)來(lái)自.NET的例子: 很多類(lèi)實(shí)現(xiàn)了IDictionary 接口。你可以使用簡(jiǎn)單的類(lèi)型轉(zhuǎn)換訪問(wèn)接口: IDictionary myDict = (IDictionary)someobjectthatsupportsit; 現(xiàn)在你的代碼可以訪問(wèn)字典了?傻鹊龋艺f(shuō)很多類(lèi)可以實(shí)現(xiàn)這個(gè)接口——所以,你可以在多個(gè)地方重用代碼來(lái)訪問(wèn) IDictionary 接口!一旦學(xué)會(huì),任何地方都可使用。 當(dāng)你決定在類(lèi)設(shè)計(jì)中使用接口時(shí),學(xué)習(xí)更多關(guān)于面向?qū)ο蟮脑O(shè)計(jì)是個(gè)好主意。這本書(shū)不能教你這些概念,但你可以學(xué) 習(xí)如何創(chuàng)建接口。以下的代碼段定義接口IFace,它只有一個(gè)方法: interface IFace { void ShowMyFace(); } 正如我所提到的,不能從這個(gè)定義實(shí)例化一個(gè)對(duì)象,但可以從它派生一個(gè)類(lèi)。因此,該類(lèi)必須實(shí)現(xiàn)ShowMyFace抽象方 法: class CFace:IFace { public void ShowMyFace() { Console.WriteLine("implementation"); } }
接口成員和類(lèi)成員的區(qū)別在于,接口成員不能被實(shí)現(xiàn)。因此,我不想在下一章中再次提到這一點(diǎn)。
4.2.4 代表元 一個(gè)代表元封裝了具有一些標(biāo)志的一個(gè)方法;旧,代表元是類(lèi)型安全和函數(shù)指針的安全版本(回調(diào)功能)?梢酝 時(shí)在一個(gè)代表元實(shí)例中同時(shí)封裝靜態(tài)和實(shí)例方法。 盡管你可以用代表員當(dāng)作具有方法,但它們的主要用途是擁有有一個(gè)類(lèi)事件。再次,我想把你引到下一章,那里會(huì)詳細(xì)地 討論類(lèi)。 4.2.5 字符串類(lèi)型 C程序員可能會(huì)詫異,但當(dāng)然,C#有一個(gè)用于操作字符串?dāng)?shù)據(jù)的基本字符串類(lèi)型。字符串類(lèi)直接派生自對(duì)象,且它是被 密封的,這意味著再不能從它派生類(lèi)。就象其它類(lèi)型,字符串是預(yù)定義類(lèi)System String的一個(gè)別名。 它的用法十分簡(jiǎn)單: string myString = "some text"; 合并字符串同樣簡(jiǎn)單: string myString = "some text" + " and a bit more"; 而如果你想訪問(wèn)單個(gè)字符,所要做的就是訪問(wèn)下標(biāo): char chFirst = myString[0]; 當(dāng)比較兩個(gè)字符串是否相等時(shí),簡(jiǎn)單地使用"=="比較操作符。 if (myString == yourString) ... 我只不過(guò)想提到,盡管字符串是一個(gè)引用類(lèi)型,比較時(shí)是比較值,而不是比較引用(內(nèi)存地址)。 字符串類(lèi)型幾乎用于這本書(shū)的每一個(gè)例子中,而且在這些例程中,我會(huì)介紹給你一些由字符串對(duì)象所顯露的極其有趣的 方法。 4.2.6 數(shù)組 一個(gè)數(shù)組包含有通過(guò)計(jì)算下標(biāo)訪問(wèn)的變量。所有包含于數(shù)組中且被當(dāng)作元素的變量必須是同一類(lèi)型。這種類(lèi)型自然被 稱(chēng)為"數(shù)組類(lèi)型"。數(shù)組可以存儲(chǔ)整數(shù)對(duì)象、字符串對(duì)象或者 你提出的任何對(duì)象。 數(shù)組的維數(shù)就是所謂的排(rank),它決定了相關(guān)數(shù)組元素的下標(biāo)數(shù)。最常用的數(shù)組是一維數(shù)組(第一排)。一個(gè)多維 數(shù)組具有的排數(shù)大于1 。每個(gè)維的下標(biāo)始于0,終于維的長(zhǎng)度減1 。 應(yīng)有足夠的理論支持。讓我們看一下用一個(gè)數(shù)組初始化器( array initializer)初始化的數(shù)組: string[] arrLanguages = { "C", "C++", "C#" }; 該簡(jiǎn)寫(xiě)效果等同以下: arrLanguages[0]="C"; arrLanguages[1]="C++"; arrLanguages[2]="C#"; 而編譯器為你做了所有的工作。當(dāng)然,它將同樣為多維數(shù)組初始化器工作: int[,] arr = {{0,1}, {2,3}, {4,5}}; 它是以下的簡(jiǎn)寫(xiě): arr[0,0] = 0; arr[0,1] = 1; arr[1,0] = 2; arr[1,1] = 3; arr[2,0] = 4; arr[2,1] = 5; 如果你不想事先初始化一個(gè)數(shù)組,但知道了它的大小,該聲明就象這樣: int[,] myArr = new int[5,3]; 如果數(shù)組的大小必須動(dòng)態(tài)地被計(jì)算,用于數(shù)組創(chuàng)建的語(yǔ)句可以象這樣寫(xiě): int nVar = 5; int[] arrToo = new int[nVar]; 正如我在這一節(jié)開(kāi)始所陳述的,你可以往數(shù)組里面塞任何東西,只要所有的元素類(lèi)型都相同。因此,如果你想把任何 東西放進(jìn)一個(gè)數(shù)組,就聲明它的類(lèi)型為對(duì)象:
4.3 加框和消框 這一章的課程中,我已經(jīng)給出了各式各樣的值類(lèi)型和引用類(lèi)型。由于速度的原因,你會(huì)使用值類(lèi)型——它除了占據(jù) 一定空間的內(nèi)存塊外,就沒(méi)有什么了。但是,有時(shí)對(duì)象的方便性就象值類(lèi)型一樣好用。 這就是加框和消框登上了舞臺(tái)的地方,加框和消框是C#類(lèi)型系統(tǒng)的核心概念。通過(guò)允許一個(gè)值類(lèi)型轉(zhuǎn)換成類(lèi)型對(duì)象 或從類(lèi)型對(duì)象轉(zhuǎn)換成值類(lèi)型,這種機(jī)制形成了值類(lèi)型和引用類(lèi)型之間的捆綁連接。任何東西終究是一個(gè)對(duì)象——但是,僅 當(dāng)需要它們是對(duì)象時(shí)。 4.3.1 加框轉(zhuǎn)換 給一個(gè)值加框指隱式地把任何值類(lèi)型轉(zhuǎn)換成類(lèi)型對(duì)象。當(dāng)一個(gè)值類(lèi)型被加框時(shí),一個(gè)對(duì)象實(shí)例就被分配,且值類(lèi)型的 值被拷貝給新的對(duì)象。 看以下例子: int nFunny = 2000; object oFunny = nFunny; 第二行的賦值暗示調(diào)用一個(gè)加框操作。nFunny整型變量的值被拷貝給oFunny對(duì)象,F(xiàn)在整型變量和對(duì)象變量都同時(shí)存 在于棧中,但對(duì)象的值居留在堆中。 那么,它暗示著什么呢?它們的值互相獨(dú)立——在它們之間沒(méi)有連接。(oFunny沒(méi)有引用nFunny的值。) 以下代碼說(shuō) 明了結(jié)果: int nFunny = 2000; object oFunny = nFunny; oFunny = 2001; Console.WriteLine("{0} {1}", nFunny, oFunny); 當(dāng)代碼改變oFunny的值時(shí),nFunny的值并沒(méi)有改變。只要你腦袋中有這個(gè)copy動(dòng)作,就能夠使用值類(lèi)型的對(duì)象功能, 發(fā)揮出你的巨大優(yōu)勢(shì)! 4.3.2 消框轉(zhuǎn)換 和加框相比,消框是顯式操作——必須告訴編譯器,你想從對(duì)象中抽取出哪一種值類(lèi)型。當(dāng)執(zhí)行消框操作時(shí),C#檢測(cè) 所請(qǐng)求的值類(lèi)型實(shí)際上存儲(chǔ)在對(duì)象實(shí)例中。經(jīng)過(guò)成功的校驗(yàn),該值被消框。
這就是消框如何執(zhí)行: int nFunny = 2000; object oFunny = nFunny; int nNotSoFunny = (int)oFunny;
如果錯(cuò)誤地請(qǐng)求一個(gè)double值 double nNotSoFunny = (double)oFunny; 通用語(yǔ)言運(yùn)行時(shí)(Common Language Runtime,簡(jiǎn)寫(xiě)CLR)將會(huì)引發(fā)一個(gè)InvalidCastException異常。你可以在第7章 "異常處 理" 中學(xué)到更多有關(guān)異常處理的知識(shí)。
4.4 小結(jié) 在這一章中,你學(xué)到了C#中用到的各種類(lèi)型。簡(jiǎn)單的值類(lèi)型包括整型、布爾型、浮點(diǎn)型和小數(shù)型。你會(huì)非常經(jīng)常地用 到一些類(lèi)型,進(jìn)行數(shù)學(xué)和金融的計(jì)算,還有邏輯表達(dá)。 在介紹引用類(lèi)型之前,我顯示了一個(gè)看起來(lái)象類(lèi)的結(jié)構(gòu)類(lèi)型。它幾乎如一個(gè)類(lèi)般地運(yùn)作,但它只是一個(gè)值類(lèi)型,這使它更 加適合需要有大量的小對(duì)象的場(chǎng)合。 引用類(lèi)型起始于所有對(duì)象之母的objedt本身。object是C#中所有對(duì)象的基類(lèi),且它同樣用于值類(lèi)型的加框和消框。除 此之外,我還讓你領(lǐng)略了代表元、字符串和數(shù)組。 令C#程序員十分神氣的類(lèi)型就是類(lèi)。它是C#面向?qū)ο缶幊痰男呐K,下一章整章專(zhuān)門(mén)讓你迅速理解這個(gè)激動(dòng)人心且功能 強(qiáng)大的類(lèi)型。
|