最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

第 79 講:C# 2 之外部別名

2022-01-12 10:48 作者:SunnieShine  | 我要投稿

本文的內(nèi)容比較特殊。它基本上在你平時使用代碼里完全用不到;相反,它更多的用途是幫助和輔助編譯器執(zhí)行和生成代碼和分析代碼。如果你想要了解這一塊的內(nèi)容,你可以試著參考本文來理解它們;如果你不想了解的話,本文的內(nèi)容是可以完全忽略,也基本上沒有問題。

Part 1 命名空間的根:global 關鍵字

要說這個詞的起源,我們就得說說,命名空間的取消命名空間二義性的問題。

命名空間是為了規(guī)劃整個項目里不同代碼的“邏輯文件夾”名稱,它不需要綁定真正的文件夾就可以完成管理和分類存儲不同功能的、不同類型的 API。但是,命名空間是我們可以隨意取名的。而我們知道,System 以及派生下來的命名空間表示系統(tǒng)自帶的 API,也就是 .NET 原生提供和支持的 API。

可是,我們也有可能在自己的項目里創(chuàng)建和帶有這樣的 API,然后用上這個 System 作為命名空間的一部分。那么,存在兩種情況:

  1. System 出現(xiàn)在你定義的命名空間的最開頭;

  2. System 出現(xiàn)在你定義的命名空間的中間,作為子命名空間名稱出現(xiàn)。

第一種非常麻煩,因此我們稍后說明;而第二種說起來要略微簡單一些,因此我們先說第二種情況。

這個第二個情況,指的是你定義命名空間的時候,用上了諸如

這樣的定義。是的,System 放在了 Sunnie.Game.System 的非最開頭的其余地方,作為子命名空間名稱出現(xiàn)。在這種情況下,你完全無法使用任何 System 命名空間下的 .NET 自帶 API(即庫 API)。

那么,你非得用的話,C# 1 是做不到的。在 C# 2 里,為了解決這一點,C# 新創(chuàng)建了一個關鍵字 global,用在命名空間的最開頭,表示你使用的是,緊挨著 global 關鍵字后的這個 System,是整個你表達的命名空間的開頭。啥意思呢?比如這樣的代碼:

請注意 global 關鍵字的用法和語法。global 關鍵字后緊跟的是兩個冒號 ::。:: 稍微放一邊,我來說一下這是什么意思。在命名空間定義的時候,我們是寫的 Sunnie.Game.System,而此時的 System 是在非最開頭的地方;而我們要想引用一個系統(tǒng)自帶的庫 API,我們必然知道,System 是作開頭的。那么,global 緊挨著的這個寫法 global::System 就基本上清楚了:它表示 System 是命名空間的開頭的類型引用。那么,我們這里 global::System.Console.WriteLine 的整個這一部分內(nèi)容下,global:: 保證的是 System 引用的是最開始就得是 System 的命名空間,而我們自己定義的命名空間名稱里,是把 System 放在非最開頭的地方的,因此從這個角度來說,我們完美區(qū)分和辨別開了兩個命名空間的引用。所以,如果你在定義命名空間的時候在非最開頭的地方使用了 System 單詞,那么你只需要在你的代碼里使用 global:: 前綴來補充說明你引用的 API 和類型,就可以說明和區(qū)分開你此時用的是庫 API。

可能你會問我,為什么會有這樣的機制?那我就來說說這一點。還記得我前文的內(nèi)容講述的內(nèi)容嗎?如果你使用 using 指令來引用了 System 命名空間的話,假設你沒有引用 System.Collections 命名空間,也可以省略掉同樣的前半截:System.。正是因為這樣的原因,命名空間在代碼使用層面來說,是可以省略和書寫簡略寫法的。因此,出現(xiàn)在代碼里的類型的使用一共有三種書寫格式:

  • 只寫類型名:這種是明確了類型的具體命名空間的時候;

  • 類型名前面帶有不全的命名空間部分:這種是引用了一部分,但沒有全部完整引用命名空間下來的時候;

  • 類型名前面帶有完整的命名空間:這種屬于完全無法確定類型的存儲位置。

從這個角度來說,因為語法的靈活性,我們只看 System 是無從確定它從哪里開始和派生,它可以是你引用了 using Sunnie.Game; 后的“產(chǎn)物”,也可以是 System 系統(tǒng) API 的命名空間。所以我們才會有 global 來區(qū)分這樣的情況。

說完了這里,我們來說一下 :: 符號。:: 是一個全新的運算符。說實話,這個運算符早就有了,C# 的原生語法里就包含這個運算符,但因為用得很少,因此我們整個教程都沒有對這個內(nèi)容作任何的提及,或者說提及相當少。::命名空間別名限定符(Namespace Alias Qualifier),專門用在命名空間的別名上。別名?是的,就是前文我們說過的命名空間別名的內(nèi)容:

不過,因為這里的 collection 是類型別名,因此我們除了可以用 . 以外,仍然可以使用 :: 來連接:

這是因為我們這里的 :: 是命名空間別名上才會用到的、將別名和正常的子命名空間名稱(或是類型名稱)連接起來的一個符號。不過,.:: 的區(qū)別是,. 是常見的,所以 C# 絕大多數(shù)時候都用 . 來連接,包括這里的命名空間別名的時候。因為命名空間別名定義下來后,它總歸也是命名空間名稱,因此我們?nèi)匀豢梢杂?C# 原生的語法規(guī)則(即使用 . 連接命名空間和類型)來完成這個工作。只是,C# 里的正常規(guī)則是,當使用命名空間別名的時候,命名空間別名要和子命名空間名稱(或者是正常類型名稱)連起來,需要用 ::。是的,區(qū)別在于,用的場景不一樣而已。

回到前文。global 關鍵字后,為什么用的是 :: 呢?這就得說一下,global 表示啥了。C# 2 里發(fā)明了這種機制,讓 global 充當和表示“命名空間的根”。換句話說,任何一個開始,都是從 global 這個邏輯的命名空間的“根”所派生下來的情況,包括我們自定義的命名空間名稱,以及庫 API 的命名空間名稱。global 實際上起到的作用是辨別和區(qū)分是不是從最開始引用的命名空間名。比如前面的 collection.ArrayList,這個 collection 就可能是開頭,也可能不是開頭。

如果一個數(shù)據(jù)類型沒有任何的命名空間的定義,那么它默認就是在 global 為別名的命名空間下的。

比如這樣的代碼。我們的實例化語句里使用的是 global::Z 這樣的引用格式來表示 Z 的完整命名空間。當然,這種情況在 C# 原生語法里是省略 global 不寫的情況,即直接寫 Z z = new Z() 的,只不過 C# 2 帶來了 global 這個機制后,這里的 Z 我們就可以通過這樣 global 是根命名空間的概念了,那么寫成 global::Z 也就不是錯誤的語法了。

那么,問題來了。global 我們?nèi)匀唤凶龅氖敲臻g別名,而命名空間別名可以用 . 也可以用 :: 來限定使用 API 的所在命名空間的??蓡栴}是,我寫 global.Z 而不是 global::Z 的時候,編譯器居然跟我說這么寫不對:

它說,無法找到類型或命名空間名稱為 global 的情況,并問你是不是少了什么 using 指令。奇了怪了。global 是關鍵字,哪里來的命名空間的引用?難不成你還寫 using global;?笑話。

事實的真相是,只能用 :: 而不能用 .。這是為什么呢?下面我們就要來說說,較為復雜的第一種情況了。

Part 2 程序集別名:extern alias 指令

如果我自己定義命名空間的時候,把 System 放在我自定義的命名空間的最開頭,像是這樣:

對,沒錯。這個假設是我自己寫代碼的時候?qū)懮先サ?,而不?.NET 的 API。為了解釋和表達我們后面的語法機制,我先來拋出一個問題,并在稍后解釋。

我們知道,System 里有 Console 類型,用來控制和封裝一個控制臺的相關內(nèi)容。而我在不小心或者不知道的情況下,自己在寫代碼的時候,自己又定義了一個 Console 類型,而且你還不小心連命名空間都寫的是一樣的:

那么,此時你寫代碼的時候,不論你寫 using System; 來引用 Console,還是使用 System.Console,都無法區(qū)分和辨別到底我想用的是自己寫的 Console 類型還是庫里自帶的 Console 類型。

比如,這樣。

這個時候,我們需要一種特殊的機制來完成這個任務:外部別名(External Alias)。

假設我們單獨使用一個項目存儲 Console 這樣的、全部以 System 作為命名空間的類型。我們試著使用引用機制,將項目和項目引用關聯(lián)起來。

假設它位于一個單獨的項目里,假設項目是這個:

接著,我們要使用這個 Console 類型。那么,我們需要引用項目,把項目引用進來:

我們選擇“添加項目引用”。

找到項目,并點擊 OK。

依次展開解決方案資源管理器的當前項目,就可以發(fā)現(xiàn),項目已經(jīng)導入進來了。

不過我們的任務沒有完。這里 Temp 項目就是我們用上了 System 的這個特殊項目。接著,我們對其點擊右鍵,選擇“屬性”。

接著,看到“別名”這一項。

在這里填一個你喜歡的標識符。這個標識符就是整個程序集的別名了。這個程序集別名就意味著你里面所使用到的任何一個數(shù)據(jù)類型,它的根的命名空間名稱都是走這個別名定義的這個名字所派生下來的。比如我這里填入了一個 another。

然后,返回到你要使用這個類型的文件。在文件的最開頭(using 指令的上面),加上 extern alias 指令,語法是這樣的:

然后請注意代碼的第 9 行。第 9 行我們使用了這個 another 名稱,然后它指向的就是我們這里自己定義的 Console 類型了;而我們還是仍然想要用 .NET 自帶的 Console 類型的話,直接書寫 Console.WriteLine 就可以了:

通過所謂的 extern alias 指令,我們就可以完美區(qū)分開不同的 System 命名空間派生的不同的類型了。我們可以看到,Visual Studio 完全可以區(qū)分開這兩種類型:

那么問題來了。我整個操作,都是基于單獨的一個 Temp 項目。這是為什么呢?為啥我講一個特性還單獨創(chuàng)建一個新的項目來解釋?這是因為,項目和項目關聯(lián)用的是 dll 后綴的文件。而系統(tǒng)自帶的 API 也是 dll 文件。也就是說,你要想使用各種各樣的 API,自然就是引用各處下載得到的這些 dll 文件,然后才可以使用起來的。而為了區(qū)分開 System 命名空間的來源,我們在演示的過程之中,將 .NET API 這個庫項目也看成了一個單獨的項目。因此,項目和項目之間的區(qū)別和區(qū)分,使用項目為單位是最合適不過的解釋辦法。

好了,本文就說到這里。外部別名就是一種取消引用二義性的機制,它只在極端情況下會存在;而解決辦法就是通過在引用 dll 文件和項目的過程之中,改變別名的方式,來完成區(qū)分不同的文件來源,就可以在代碼里區(qū)分開來了。

欸,這文章還沒完啊。好像還沒解釋 another 這個別名后面為啥是雙冒號 :: 而不是小數(shù)點 .。我一句話來說明清楚原因:這種別名是特殊的,這就是我前文提到的“絕大多數(shù)情況”的對立情況。唯有這一種情況下,我們必須使用雙冒號 :: 來限制和引用命名空間,而 . 在這里是行不通的。那么,為什么呢?你可以仔細對比 another 這里的用法,以及前面說到的 global 關鍵字的用法。對比起來就可以發(fā)現(xiàn),其實它們的機制是完全相同的——你完全可以把前面提到的 global 想象成默認的命名空間的根;而如果你創(chuàng)建了自己的 dll 文件或項目的引用的話,你可以為 dll 文件引用或項目引用單獨取一個自己定義的名字。這樣就可以避開使用 global,而是使用別的名字,來引用不同的類型。從另外一個方面來說,不論是 global 也好,還是 another 也好,它們都是整個 dll 文件或項目引用的命名空間的根,而且它還是以別名出現(xiàn)。因此,這里我們需要用,也只能用 :: 來限定。只有這一種情況下,:: 不能換成 .。


第 79 講:C# 2 之外部別名的評論 (共 條)

分享到微博請遵守國家法律
大名县| 玉山县| 和静县| 延川县| 柞水县| 安福县| 玉溪市| 社旗县| 礼泉县| 准格尔旗| 内乡县| 油尖旺区| 卢氏县| 静乐县| 霍城县| 六枝特区| 泸州市| 峨眉山市| 科技| 铁力市| 新和县| 托克托县| 霞浦县| 全椒县| 香格里拉县| 临西县| 新昌县| 淄博市| 循化| 吉林省| 邢台市| 西峡县| 庆云县| 肥城市| 施秉县| 古交市| 宁强县| 阳朔县| 修水县| 确山县| 元氏县|