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

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

第 95 講:C# 3 之分部方法

2022-03-17 08:45 作者:SunnieShine  | 我要投稿

下面我們要介紹兩個(gè)方法的語法拓展特性:分部方法和擴(kuò)展方法。先來看簡單的:分部方法(Partial Method)。

Part 1 partial 用在方法上

考慮一種情況。假設(shè)我對(duì)這個(gè)類型實(shí)現(xiàn)了眾多的情況,它們完成的任務(wù)各不相同。

比如我有一個(gè) Work 類型,里面包含 Do 方法表示完成一個(gè)任務(wù)。這個(gè)任務(wù)有眾多的步驟需要我們挨個(gè)完成。我們假設(shè)它們分別表示為不返回任何數(shù)值的 Step1、Step2Step3 方法(當(dāng)然也可以有 Step4 等等,這里就假設(shè)三個(gè))。

問題是,我把這個(gè)實(shí)現(xiàn)過程的步驟定義好了,卻在發(fā)現(xiàn)計(jì)劃只完成了一半。另一半則是實(shí)現(xiàn) Step1、Step2、Step3 方法。但問題是,有些時(shí)候我們不總是非得需要將所有的步驟全給實(shí)現(xiàn)了,比如調(diào)試的時(shí)候。假如我只是想試著調(diào)試運(yùn)行一下前面的步驟是否成功執(zhí)行的話,那么此時(shí)可能 Step1、Step2 由我們實(shí)現(xiàn)完成,而 Step3 是沒邏輯的。

那么,代碼可以成這樣:

此時(shí)我們不得不屏蔽 Step3,或是試著實(shí)現(xiàn)一個(gè)空的 Step3 方法。

不過有些時(shí)候,這樣的實(shí)現(xiàn)并不是完美的。因?yàn)樗粔蜢`活:我們必須屏蔽一些我們暫時(shí)不需要的代碼,或者是強(qiáng)制性地實(shí)現(xiàn)一些地方的代碼,只不過給大括號(hào)里寫成空的而已。于是,分部方法就誕生了。

我們試著替換 private 關(guān)鍵字,改成 partial 關(guān)鍵字,然后把實(shí)現(xiàn)代碼單獨(dú)放在一個(gè)文件里,而聲明則放在當(dāng)前文件里。舉個(gè)例子,假設(shè)我主要實(shí)現(xiàn)邏輯(包含 Do 方法的這個(gè)文件)放下面的這些代碼:

注意兩個(gè)地方,一個(gè)是第 2 行我們給類型添加了 partial 關(guān)鍵字;而第 12 到第 14 行代碼的寫法則以分號(hào)結(jié)尾。然后我們試著創(chuàng)建一個(gè)新的文件叫 Work.Impl.cs,表示實(shí)現(xiàn)部分。此時(shí) Step3 方法我們不實(shí)現(xiàn),而只實(shí)現(xiàn)前兩個(gè)方法。

這個(gè)文件我們只包含 Step1 方法和 Step2 方法的邏輯,而 Step3 我們不去關(guān)心它有沒有實(shí)現(xiàn)。注意關(guān)鍵字使用:因?yàn)轭愋头譃閮蓚€(gè)文件存儲(chǔ),一個(gè)是定義,一個(gè)是實(shí)現(xiàn),所以文件都得帶上 partial 修飾符暗示兩個(gè)類型是同一個(gè)類型,但放在不同的文件里。然后在實(shí)現(xiàn)的方法上,將 private 改為 partial。不過一個(gè)文件表示的是定義方法的簽名,而帶 Impl 后綴的文件,則是去實(shí)現(xiàn)我們需要實(shí)現(xiàn)的部分。

這個(gè)語法是 C# 3 誕生的新語法:分部方法。分部方法允許我們在同一個(gè)類型里將方法分為兩個(gè)文件存儲(chǔ):定義和實(shí)現(xiàn)。分離定義和實(shí)現(xiàn),是分部方法的目的和效果。

Part 2 為什么設(shè)計(jì)這么古怪?

可能你會(huì)覺得,這種設(shè)計(jì)的目的和意義。好吧實(shí)際上這么設(shè)計(jì)代碼是有用途的。我們最常用的一個(gè)點(diǎn)是,分部方法可以不實(shí)現(xiàn),即提前用簽名來調(diào)用方法。如果方法沒有實(shí)現(xiàn),編譯器會(huì)將其自動(dòng)刪除掉。

考慮前面的代碼。我們?nèi)绻@么去設(shè)計(jì)一個(gè)類的話,我們可以定義一大堆的 partial void 方法(參數(shù)) 的簽名,但不去實(shí)現(xiàn)它。我們可以只挑選其中一些去實(shí)現(xiàn),它也不影響代碼的運(yùn)行,編譯仍舊是通過的。就像是上面給的這個(gè)例子里,partial void 方法(參數(shù)) 格式語法的地方有三處,分別是 Step1Step2Step3 這三個(gè)方法的簽名定義。它們定義后直接以分號(hào)就結(jié)尾了。只要我們實(shí)現(xiàn)了它,那么在 Do 方法里就會(huì)執(zhí)行;相反如果不實(shí)現(xiàn),那么你不更改 Do 里的代碼也是 OK 的,但是因?yàn)樗鼪]有實(shí)現(xiàn),因此會(huì)被編譯器自動(dòng)刪除。畢竟反正也沒給出實(shí)現(xiàn),保留也沒有意義。

Part 3 分部方法的限制

這種設(shè)計(jì)是有道理的,但正是因?yàn)橛羞@種設(shè)計(jì)規(guī)定,因此也確實(shí)擁有很多限制。下面我們來說一下它們。

3-1 分部方法默認(rèn)為 private 級(jí)別;不允許修飾訪問修飾符

可以看出,這樣的代碼是類型內(nèi)級(jí)別訪問。因?yàn)樗挥脕韮?nèi)部討論和操作,如果暴露到任何別的地方的話,都會(huì)導(dǎo)致濫用和錯(cuò)誤使用,因此這種限制是有必要的。

C# 3 的分部方法語法規(guī)定,我們給的分部方法默認(rèn)就是私有的。而正是因?yàn)樗悄J(rèn)的也是不可改變的,因此分部方法甚至不允許顯式地給出任何訪問修飾符的關(guān)鍵字,即使是 private 也不行。

3-2 分部方法必須不返回任何數(shù)值

C# 3 也不允許分部方法返回任何數(shù)值。這是因?yàn)?,返回值可能?huì)改變方法的執(zhí)行行為。比如說下面的代碼:

假設(shè) F 是分部方法的話,那么如果方法沒有給予實(shí)現(xiàn)部分,那么這個(gè) result 就不可能存在。因?yàn)?F 會(huì)被編譯器自動(dòng)刪除,但 result 的牽連會(huì)使得編譯器處理起來困難不少——我后面所有使用 result 的地方也都得刪除,說不定任何用到 result 的地方,也會(huì)被改變邏輯和執(zhí)行意義。因此,這樣的問題是顯著的,因此,C# 3 分部方法不允許返回值。正是因?yàn)檫@個(gè)原因,partial 關(guān)鍵字后必須跟 void 關(guān)鍵字。

3-3 分部方法不能帶有 out 參數(shù)

既然返回值都不允許了,那么輸出參數(shù)也肯定不能被允許。因?yàn)?out 修飾的參數(shù)是從參數(shù)位置去返回的類型,它解決的是方法只返回一個(gè)數(shù)的問題。

顯然這樣的代碼,編譯器處理起來肯定也是比較棘手的。因此,這樣的代碼也不被允許。不過請(qǐng)注意,分部方法僅僅是不允許 out,而 ref 是可以修飾的。有人可能會(huì)問,為啥 ref 可以?ref 不是也是跟 out 差不多,只是 ref 雙向,而 out 只是輸出嗎?其實(shí),你自己仔細(xì)思考一番,就可以知道為什么了。請(qǐng)看下面的代碼。

注意分部方法的實(shí)現(xiàn),參數(shù)是有 ref 修飾符的。

下面我們調(diào)用它。

顯然,這樣的代碼是合理的??蓡栴}就在于,如果我們不給出實(shí)現(xiàn),那么編譯器就會(huì)自動(dòng)刪除 F 的調(diào)用:

這并不影響編譯。

是的,這樣的代碼因?yàn)?x 并未和方法關(guān)聯(lián)起來,傳入引用也只是是理論上可以在 F 方法里改變 x 的數(shù)值,但實(shí)際上,如果我不調(diào)用它的話,編譯器也知道,F(ref x) 調(diào)用被刪除不影響代碼的編譯。

這里插一句你可能沒有注意過的細(xì)節(jié)。不論是不是分部方法,所有方法里只要帶有 ref 參數(shù)的方法,那么編譯器都會(huì)要求你調(diào)用傳參之前必須有初始值。比如說這里的 int x = 20;,如果在調(diào)用 F(ref x) 之前沒有給 x 賦初始值,編譯器會(huì)直接報(bào)錯(cuò)。因?yàn)檫@樣的調(diào)用過程就有可能因?yàn)閷?shí)現(xiàn) F 方法里改動(dòng)了它的數(shù)值。因?yàn)榉椒w里是無法知道 x 是否有初始化。如果 x 沒初始化就在使用,這必將導(dǎo)致代碼執(zhí)行起來的不安全性。因此為了嚴(yán)謹(jǐn),編譯器會(huì)要求你必須對(duì) ref 參數(shù)傳參的這個(gè)變量先具有初始值之后才可以使用起來。

3-4 分部方法修飾符只允許 staticunsafe;別的都不行

C# 誕生了各種各樣的類型后,也擁有了完成的繼承體系。在 C# 里,關(guān)于繼承的關(guān)鍵字有這些:

  • sealed 關(guān)鍵字;

  • abstract 關(guān)鍵字;

  • virtual 關(guān)鍵字;

  • override 關(guān)鍵字;

  • new 關(guān)鍵字。

不過,這些關(guān)鍵字全部都不能用于分部方法。原因很簡單:因?yàn)槔^承機(jī)制無法防止你的實(shí)現(xiàn)的派生和重寫,導(dǎo)致代碼的不安全。舉個(gè)例子:

這種就是典型的錯(cuò)誤使用。因?yàn)?F 本身就是抽象的了,它本身就不應(yīng)該有實(shí)現(xiàn)部分。但是,你對(duì)這種本身就抽象的東西修飾 partial 是什么意思?分部方法意味著你可以按需在別的文件里給出 F 的實(shí)現(xiàn),但 abstract 修飾符又防止你在這個(gè)類型里去實(shí)現(xiàn)它,這不是自相矛盾的嗎?

在派生和繼承機(jī)制下,上述五個(gè)修飾符全部都不行,別的四個(gè)你就自己分析思考一下為什么吧。

哦對(duì),extern 關(guān)鍵字也不行,雖然這個(gè)修飾符用于導(dǎo)入 dll 文件里帶有的庫函數(shù)。但是,這樣的方法本身就不可能有實(shí)現(xiàn),因?yàn)閷?shí)現(xiàn)并不在 C# 語言里實(shí)現(xiàn),而是在 dll 文件里,因此它本身就沒有實(shí)現(xiàn)機(jī)制,那么 partial 就沒有意義了。

所以,上述的修飾符一個(gè)都不行。除了 static。

這就是一個(gè)寫法,也是目前唯一一個(gè)可以使用的修飾符,因?yàn)檫@個(gè)修飾符只標(biāo)識(shí)這個(gè)方法的靜態(tài)與否,跟執(zhí)行沒有任何影響和牽連。當(dāng)然,unsafe 也可以。

這樣就行。用法也很簡單。

比如這樣。

3-5 分部方法必須用于分部類型里

這個(gè)是顯而易見的。你要用分部類,那么你有可能會(huì)對(duì)這個(gè)方法給予實(shí)現(xiàn)。那么既然有可能提供實(shí)現(xiàn),那么自然就需要一個(gè)環(huán)境提供這樣的行為可以使用。那么分部方法必須放在分部類型里就先得非常正常了。

Part 4 泛型分部方法

分部方法牛逼就牛逼在它可以使用泛型??紤]內(nèi)部排序方法:

這便是一個(gè)實(shí)現(xiàn)。假設(shè)我使用接口來完成這個(gè)任務(wù)的話,那么我們需要設(shè)置泛型約束。

可以從這個(gè)例子里看出,我們具有的泛型參數(shù)必須是值類型,而且還得是實(shí)現(xiàn)了大小比較行為的類型,這樣我們才能確保操作能夠大小比較成功,并且交換成功。

顯然,調(diào)用方就不多說了。這里關(guān)注一下代碼的寫法。這里要注意兩個(gè)地方。

4-1 泛型參數(shù)是可以換名字的

可以看到,泛型參數(shù)在定義里用的是 TComparable,而實(shí)現(xiàn)里卻用的是 T。這是允許的。原因是編譯器對(duì)泛型的分部方法如何看待的。編譯器對(duì)泛型參數(shù)的名字并不關(guān)心,它只關(guān)心泛型參數(shù)的名字是否有重復(fù),個(gè)數(shù)有幾個(gè),都對(duì)應(yīng)哪里。舉個(gè)例子,A<T>A<TypeParameter> 是同一個(gè)類型,雖然泛型參數(shù)名字不同,但大家都知道,泛型參數(shù)在底層的實(shí)現(xiàn)機(jī)制,是看個(gè)數(shù)不同來區(qū)分類型的。因此,對(duì)于上面的分部方法來說,泛型參數(shù)的名字換沒換其實(shí)并不重要。

4-2 泛型參數(shù)的約束是必須重復(fù)的

雖然泛型約束不是簽名的一部分,但只在簽名里書寫,或者只在實(shí)現(xiàn)里書寫泛型約束,都是不夠明顯的。因此,你必須重復(fù)寫泛型約束,便于編譯器分析的同時(shí)還能更輕松地掌握?qǐng)?zhí)行過程。

比如這樣,第 8 行由于缺少泛型約束,因此不正確。

不過,這一點(diǎn)在官方要求的文檔里并不是這么寫的。C# 的官方文檔里說,“泛型約束用于分部方法的定義部分,但實(shí)現(xiàn)部分是可以不寫的”。

但事實(shí)是,你這么寫代碼,會(huì)收到編譯器錯(cuò)誤,告訴你“定義和實(shí)現(xiàn)上,泛型約束有所不同”。

是的,就是這么離譜。

Part 5 委托引用分部方法的細(xì)節(jié)

考慮如下代碼:

其中 F 方法有實(shí)現(xiàn),但 G 方法沒有。有些人會(huì)為了去鉆空子去試試看,有沒有辦法引用到 G 方法并調(diào)用,于是考慮到了委托。

請(qǐng)問,這樣的代碼對(duì)不對(duì)?答案肯定不對(duì),而且是第 2 行必然報(bào)錯(cuò)。原因很簡單,因?yàn)?G 方法沒有實(shí)現(xiàn),因此不論如何你都是沒有辦法去調(diào)用它的。哪怕是委托意味著“我們稍后調(diào)用”,但也由于沒有實(shí)現(xiàn),因此這樣的機(jī)制是不成立的。

因此,分部方法也是做了一個(gè)相當(dāng)嚴(yán)謹(jǐn)?shù)拇胧┤シ乐挂匀魏涡问秸{(diào)用。


第 95 講:C# 3 之分部方法的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國家法律
安陆市| 湟源县| 鄂伦春自治旗| 萝北县| 新郑市| 老河口市| 泰顺县| 邯郸县| 佛冈县| 新丰县| 双鸭山市| 石城县| 汶上县| 陈巴尔虎旗| 出国| 大兴区| 井陉县| 枣强县| 宣化县| 手游| 高阳县| 江城| 施甸县| 赣榆县| 讷河市| 太谷县| 平乡县| 金坛市| 丰台区| 南投县| 阳东县| 兖州市| 伊金霍洛旗| 正定县| 商洛市| 泸定县| 长丰县| 庄浪县| 张家港市| 甘泉县| 深水埗区|