第 78 講:C# 2 之類型和命名空間別名
總算是結(jié)束了委托的相關(guān)知識點(diǎn)了。下面我們來看一個(gè)新的語言特性:類型和命名空間別名。
1-1 回顧命名空間的語法
命名空間一直是我這個(gè)教程里最“疏忽”的知識點(diǎn)。之所以這么說,是因?yàn)檎麄€(gè)教程對命名空間的講解都相當(dāng)少。因?yàn)椋F(xiàn)代 C# 編程基本都無需用戶針對命名空間和庫 API 的所處命名空間有任何的關(guān)心,就可以寫代碼。因?yàn)?Visual Studio 現(xiàn)在已經(jīng)可以幫助用戶添加任何的命名空間的信息了,因此你完全不必去記住它們。不過,為了學(xué)習(xí) API,更加穩(wěn)妥和經(jīng)驗(yàn)性的學(xué)習(xí)辦法,仍舊是建議大家記住一些基本 API 的所處命名空間,這樣你在使用的時(shí)候就可以做到得心應(yīng)手了。
我們來回顧一下命名空間的基本知識點(diǎn)。命名空間是使用標(biāo)識符搭建起來的項(xiàng)目代碼文件管理機(jī)制。就好比你使用文件夾來分離不同功能的 API 一樣,命名空間則是完全不依賴于文件夾,而單獨(dú)創(chuàng)建命名空間的形式來區(qū)分和辨認(rèn)不同代碼文件的一種語言機(jī)制。
文件夾可以包含子文件夾,那么命名空間也可以包含子命名空間。命名空間采用標(biāo)識符取名的規(guī)則得到一個(gè)完整的命名空間名,而下屬的子命名空間名則使用小數(shù)點(diǎn) .
后跟上子命名空間名稱的方式來完成組合表示,類似于文件夾目錄的 C:\Users\Sunnie\Desktop
里面的反斜杠 \
。
聲明命名空間的方式是使用 namespace
關(guān)鍵字,后跟上命名空間的方式來對文件進(jìn)行聲明;而這種聲明方式下,我們使用大括號定界,將屬于這個(gè)命名空間的所有內(nèi)容(類型之類的)包裹在其中,構(gòu)成一個(gè)文件。像是這樣:
比如這樣的代碼。
一個(gè)文件可以包含多個(gè)命名空間的聲明,也可以只有一個(gè)。一般來說,一個(gè)文件都只包含一個(gè)命名空間的聲明。如果包含多個(gè)的話,多半是嵌套存在的:
TestProject.MainProgram
所以,很少用到多命名空間聲明在同一個(gè)文件下的情況。
1-2 命名空間別名
命名空間的別名指的是給命名空間換一個(gè)名字。它和 C 語言里的 typedef
有些類似,只不過這里,我們說的是命名空間而不是類型而已。
C# 2 允許我們使用 using
別名指令來完成,語法是這樣的:
我們來舉例說明吧。
C# 里的所有泛型集合規(guī)定的命名空間名稱是位于 System.Collections.Generic
命名空間里,那么我們要想使用 List<>
集合的話,我們需要寫 using System.Collections.Generic;
指令才能使用。不過現(xiàn)在我們有了這個(gè)機(jī)制,我們可以稍微簡化一下:
還是有點(diǎn)復(fù)雜是吧?確實(shí)是這樣,不過這個(gè)機(jī)制是伴隨著類型別名一起出來的,因此我們接著來看看類型別名的語法。
Part 2 類型別名
類型別名是專門用于類型取別名的機(jī)制,它是貨真價(jià)實(shí)跟 typedef
的 C# 翻版代替。
2-1 基本用法
我們可使用和前文一樣的定義方式,來完成對一個(gè)數(shù)據(jù)類型的別名的定義。但是一定請注意,using
別名指令和 using
System.Collections.Generic.List<System.Int32>
的地方,都可以替換為 Integers
這里我們要稍微說一個(gè)點(diǎn)。我們在書寫 using
別名指令的時(shí)候,因?yàn)橐耆珜?yīng)上一個(gè)具體的數(shù)據(jù)類型,因此你必須要寫全你對應(yīng)的數(shù)據(jù)類型,它的所在命名空間以及它的名字。比如上面舉例用到的 List<>
集合,你必須要給出它所處的命名空間才行。否則,編譯器也不能確定這個(gè) List<>
到底在哪里。
另外,你要想使用像是這樣的泛型數(shù)據(jù)類型的話,你甚至必須給出整個(gè)泛型數(shù)據(jù)類型指向的每一個(gè)泛型參數(shù)的對應(yīng)類型名稱的全名。當(dāng)然,內(nèi)置類型是可以寫關(guān)鍵字的,因?yàn)檫@個(gè)編譯器也知道它們都對應(yīng)什么類型;但普通的、自己實(shí)現(xiàn)的數(shù)據(jù)類型可能就必須指定清楚了。比如前文給的這個(gè) System.Collections.Generic.List<System.Int32>
就可以直接用關(guān)鍵字:System.Collections.Generic.List<int>
。
比如這樣的數(shù)據(jù)類型,對應(yīng)的是 Dictionary<,>
泛型集合,傳入的泛型參數(shù)分別是 Student
數(shù)據(jù)類型(由我們自己實(shí)現(xiàn),假設(shè)它位于 TestProject.Models
命名空間下),然后給予該同學(xué)成績,用 double
類型的數(shù)組表示。所以,整個(gè)這么長的類型就可以通過我們自定義的 StudentsScoreList
來代替。如果你在當(dāng)前文件的別處用上了這樣的 Dictionary<Student, double[]>
的定義類型的話,它將可以用 StudentsScoreList
來代替掉。
它的目的解決的就是類型較長而且非常不方便書寫的時(shí)候,來進(jìn)行簡化的格式和語法。像是上述這種,我們會在泛型數(shù)據(jù)類型的泛型參數(shù)上使用各種“奇怪”的數(shù)據(jù)類型,這樣的語法就能夠派上用場。
2-2 類型別名僅支持對普通和泛型數(shù)據(jù)類型的定義
如題,這種語法機(jī)制只針對于普通的(非泛型數(shù)據(jù)類型)和泛型數(shù)據(jù)類型有效果。你不能用在一個(gè)數(shù)組上,也不能用在一個(gè)可空值類型上,更不能用于指針類型上:
因?yàn)?,這樣的定義一般都沒有實(shí)際意義,因?yàn)樗鼈冏约壕妥詭в浱枺ㄖ羔樀?*
記號、可空值類型的 ?
記號、數(shù)組的 []
記號),所以我們已經(jīng)都假設(shè)它們是簡易的數(shù)據(jù)類型了,因此它們自身沒必要取別名。
不過,你可以用在一個(gè)泛型數(shù)據(jù)類型,而且泛型數(shù)據(jù)類型的泛型參數(shù)上可以出現(xiàn)它們(當(dāng)然,指針類型不能用在泛型參數(shù)上,這個(gè)是泛型機(jī)制自身的限制,并不屬于這里語法的限制),比如剛才這個(gè)例子。
這里的 double[]
就出現(xiàn)在了泛型參數(shù)上,而這樣書寫的代碼是允許的。
可能你現(xiàn)在覺得你自己書寫的代碼還不夠復(fù)雜所以不必這么簡寫,當(dāng)你學(xué)到后面 LINQ 機(jī)制的時(shí)候,大量使用到的接口類型,那時(shí)候的泛型接口還嵌套了一個(gè)泛型接口,你甚至還需要外面加一層泛型的數(shù)據(jù)類型,這時(shí)候你就知道這個(gè)機(jī)制有多么好用了。
2-3 注意類型名稱沖突現(xiàn)象
假設(shè)我現(xiàn)在定義了類型的別名 A
,結(jié)果系統(tǒng)也有一個(gè)自帶的類型也叫 A
,此時(shí)就無法區(qū)分這樣的情況。舉個(gè)例子,假設(shè)我自己創(chuàng)建了一個(gè) ArrayList
類型是別名的情況,而由于 C# 自帶 ArrayList
類型:
ArrayList
就無法區(qū)分到底是具體哪一個(gè)數(shù)據(jù)類型。這種現(xiàn)象就是類型別名沖突的現(xiàn)象。這種編譯器會自動告知用戶這么取別名是不合適的。如果你確實(shí)要用系統(tǒng)的 ArrayList
System.Collections
的 System
的情況,你在書寫代碼的時(shí)候,可省略 System