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

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

第 36 講:面向?qū)ο缶幊蹋ò耍豪^承控制

2021-05-12 09:44 作者:SunnieShine  | 我要投稿

上文我們對(duì)繼承的基本概念,以及語法格式作出了說明,其實(shí)還是不算特別難的知識(shí)點(diǎn)。那么下面我們深層次繼續(xù)討論關(guān)于繼承機(jī)制的控制,來靈活處理和約束代碼,達(dá)到良好的編碼習(xí)慣。

Part 1 base 引用

之前我們說過 this 引用。this 引用表達(dá)的是“當(dāng)前這個(gè)類里的成員的使用”。因?yàn)樗鼘懖粚懸话愣紵o所謂,只要不會(huì)和比如參數(shù)、臨時(shí)變量重名必須指定 this. 以外,其它情況是用不到的。

下面我們說說 base 引用的存在。basethis 的地位基本上差不多,只是 base 是用來表達(dá)“基類型的成員”。按道理講,我們一旦寫了繼承的語法 : 類名,那么基類型的成員就可提供給子類型使用。

舉個(gè)例子,假設(shè) Person 類型是基類型,Student 是從 Person 類繼承下來的子類型。那么假設(shè) Person 類含有 Name 屬性的話,子類型使用下面哪一個(gè)(或者哪一些)寫法才是對(duì)的呢?

  • this.Name

  • base.Name

  • Name

如果你能猜到結(jié)果,就說明你基本上對(duì)面向?qū)ο蟮睦^承機(jī)制有一定了解了。

答案是,三個(gè)都對(duì)。而且三個(gè)寫法沒有任何語義上的差別。this.Name 這么寫是有道理的:因?yàn)榛愋偷某蓡T都可以拿來給子類型用了,那當(dāng)然就是自己的東西了。所以,this.Name 是對(duì)的;base.Name 是正統(tǒng)寫法,因?yàn)楸緛砭褪腔愋偷某蓡T拿下來用的,歸結(jié)到底,其實(shí)也是基類型的成員。語法上子類型根本沒有寫 Name,所以寫 base.Name 肯定是對(duì)的;Name 也是對(duì)的。因?yàn)槭紫任覀儾粫?huì)遇到重名的問題,畢竟我們按照取名的規(guī)范和約定,可能和成員重名的只有參數(shù)和臨時(shí)變量而已。但是參數(shù)和臨時(shí)變量都是小寫開頭的駝峰命名法,而屬性卻是大寫開頭的帕斯卡命名法。你說說,這怎么可能能重名。所以,Name 也是對(duì)的。

是的,這一節(jié)就只是想告訴你,base.Name 這樣的書寫格式。但是因?yàn)闃O其不常用,因此你當(dāng)相聲聽就完事了。實(shí)際上,這種語法本來不是很常用,但它也不是不重要的東西。下一節(jié)內(nèi)容我們會(huì)講到成員的重寫,就會(huì)大量用到 base. 的語法。但是 this. 確實(shí)很少用。

Part 2 訪問修飾符在繼承里的效果

2-1 繼承關(guān)系下的 private 訪問修飾符

很高興我們說到這個(gè)知識(shí)點(diǎn)了。上一節(jié)的內(nèi)容我們并沒有說這一點(diǎn)。

假設(shè)我們有一個(gè) Student 類和 Person 類。如果 Person 類包含了一個(gè) private 修飾的方法 Hello,那么 Student 類的實(shí)例是否可以使用 stu.Hello() 的類似語法呢?

很抱歉,不能。還記得 private 修飾符的規(guī)則嗎?是的,private 修飾的成員僅僅在當(dāng)前這個(gè)類的范圍里可以隨便用。只要出了這個(gè)類,不管在哪里,都是不可以用的。就算是繼承也一樣。因?yàn)槟愕睦^承是把上面的成員直接拿下來用,但 private 修飾符意味著出了這個(gè)類你就看不見了,所以你拿下來的 Hello 成員是不可使用的狀態(tài)。

所以,在繼承關(guān)系里,private 成員是不可繼承下來的;但是,internalpublic 修飾的成員是可以的。

2-2 protected 關(guān)鍵字

那么,考慮一種情況。假設(shè)我前面設(shè)計(jì)的 Student 類和 Person 類還是保留下來,Person 里的那些字段是被 private 修飾過的,因此 Student 類里無法直接為其賦值。于是,我們只能使用前面那種“通過屬性”的形式來對(duì)字段間接賦值了。

確實(shí)可以,但問題在哪里呢?private 類型的成員只在類里可用,那么我們可以稍作推廣,如果只在往下派生的這個(gè)繼承鏈條上可用,別的地方不可用的話呢?這就是我們 protected 這個(gè)新的修飾符的用途了。

protected 修飾符專門表達(dá)“往下繼承的任何類都可以用”。比如說 A 類型往下派生出 B 類型。A 類型有一個(gè) protected 修飾的成員 _name,那么 B 類型是可以看到的。但是,除此之外,別的任何地方都看不到。

我們把之前的代碼稍加改動(dòng):

如果我們這么寫了的話,下面的類型 Student 就可以直接對(duì) _name 這些字段直接賦值了。但是,如果你從外部實(shí)例化出來的 Person 或者 Student 類型的實(shí)例,都是不可以直接使用這些 protected 修飾的字段的,因?yàn)槭菑耐獠吭L問的。

2-3 protected internal 組合關(guān)鍵字

C# 是相當(dāng)嚴(yán)謹(jǐn)?shù)恼Z言。訪問修飾符都能玩出花樣。是的,protected 還能和 internal 組合使用你敢信。

因?yàn)榍懊嫖覀冋f過,protected 只用在派生類上,所以別的地方都不可用。我們稍微擴(kuò)大一點(diǎn)范圍,如果整個(gè)項(xiàng)目里使用的話呢?這就要用到這里的組合關(guān)鍵字 protected internal 了。

protected internal 組合關(guān)鍵字是 protectedinternal 效果的復(fù)合。它既包含有繼承鏈條的用法,還包含項(xiàng)目里隨便用的用法。是的,如果我們把鏈條畫成一條直線,而把項(xiàng)目畫成一個(gè)圓的話,那么 protected internal 大概就長這樣:

是的,假設(shè)紅色線條是派生的子類型的存儲(chǔ)位置,而圓圈表達(dá)的是范圍的話,protected internal 就是“項(xiàng)目圓圈 + 線條”這個(gè)范圍的總和。但是,除了這個(gè)“線條 + 項(xiàng)目圓圈”的范疇,都是看不見這個(gè)成員的。

Part 3 繼承鏈控制

readonly 這類修飾符一樣,C# 還有一些提供用來專門規(guī)范化處理調(diào)用和繼承這樣行為的約束性質(zhì)關(guān)鍵字。代碼加不加它們其實(shí)無所謂,因?yàn)樗⒉挥绊懘a的執(zhí)行,但添加了它們可以讓代碼更為規(guī)范化。

下面我們要說兩個(gè)關(guān)鍵字類型,這兩個(gè)關(guān)鍵字可以直接用在類的上面修飾,用來表達(dá)特殊的限制和約束。

3-1 abstract 類修飾符

我們知道,Student 類是走 Person 類派生出來的類型。而 Person 作為對(duì)象的抽象體現(xiàn)和提取,我們有時(shí)候也沒有必要單獨(dú)對(duì) Person 類型直接實(shí)例化。比如我寫了三種不同的類,同時(shí)走 Person 類派生:StudentTeacherWorker。它們?nèi)慷技由狭?: Person 的語法來繼承。

那么,既然我們有了這么多子類型,顯然子類型既然創(chuàng)建出來,就是為了提供類型的使用和實(shí)例化的。那么此時(shí)的 Person 就成了“數(shù)據(jù)的提供方”了。顯然此時(shí)我們有了這些細(xì)致的類型后,這個(gè) Person 就變得沒有必要實(shí)例化。

此時(shí),我們可以為 Person 上追加 abstract 關(guān)鍵字,以表示這個(gè)類型不再可以使用實(shí)例化的語句而產(chǎn)生實(shí)例。

注意第 1 行,我們追加了 abstract 后,就不再可以使用 Person p = new Person() 的類似語法了。取而代之的是,我們只能為下面 Student、TeacherWorker 使用實(shí)例化語句 Student s = new Student(...) 之類的。

按照規(guī)范化的要求,我們建議所有的“數(shù)據(jù)提供方”都用 abstract 修飾。比如說我們建議這里的 Person 要用 abstract 修飾,來避免別人在使用的時(shí)候,對(duì) Person 這個(gè)純粹是為了提供數(shù)據(jù)用的類來實(shí)例化。

這里稍微注意一點(diǎn)。由于抽象類型本身不能實(shí)例化(調(diào)用 new 語句),因此我們無法讓抽象類從實(shí)例類型進(jìn)行派生。比如假設(shè) Human 是一個(gè)實(shí)例類型(可以實(shí)例化的那種),而 PersonHuman 類型派生,但 Person 如果是抽象類的話,就不行。

3-2 sealed 類修飾符

同理,有阻止實(shí)例化,就有阻止繼承。顯然,Student 是最下面的類型了。要知道類的繼承是可以不限制層數(shù)的,A 可以從 B 派生,而 B 很可能還從 C 派生。按照這種道理,Student 這樣的類型顯然就已經(jīng)不再需要給下面繼續(xù)提供繼承關(guān)系的使用了。比如我難道還要給個(gè)類型從 Student 類派生嗎?沒有必要了吧。這個(gè)時(shí)候我們可以對(duì) Student 類的聲明上添加 sealed 關(guān)鍵字以表達(dá)“繼承關(guān)系到我這里就結(jié)束了”。

sealed 修飾符除了約束和限制不再可以繼續(xù)繼承外,就沒有別的效果了。它并不會(huì)影響程序的執(zhí)行,換言之,你寫不寫 sealed 程序都可以跑起來,但是添加這個(gè)關(guān)鍵字可以讓別人在使用的時(shí)候明白和知道這個(gè)類不是拿來繼承的。

Part 4 我不要基類型的成員了:new 成員修飾符

我們往往在繼承的時(shí)候會(huì)遇到這么一種情況:基類型提供的某個(gè)(或某些)成員對(duì)于現(xiàn)在這個(gè)類型來說完全就是沒必要的,此時(shí)原來的成員就需要我們給完全覆蓋掉。然后我們可以對(duì)這個(gè)成員標(biāo)記 new 關(guān)鍵字來覆蓋掉原來的成員。

舉個(gè)例子。

基類型有一個(gè) ShowDetails 方法用來輸出一行固定的文字“標(biāo)準(zhǔn)的交通工具”。可問題是我現(xiàn)在買了一輛敞篷跑車(指的是這里的 ConvertibleCar : Car 的繼承關(guān)系),車頂是可以打開的。我總不能還說我這個(gè)車是標(biāo)準(zhǔn)交通工具吧。于是,原來的 ShowDetails 就得給爺爬。

可是,我直接寫 public void ShowDetails() 的時(shí)候,編譯器會(huì)提供警告信息,告訴你“你在隱藏基類型的 ShowDetails 方法”。

那么這句話是啥意思呢?這句話是在說,“因?yàn)榛愋鸵灿幸粋€(gè)完全同名的方法 ShowDetails,因此如果你不用它的話,需要追加 new 關(guān)鍵字來表達(dá)‘基類型的 ShowDetails 被我忽略掉了’”。

所以,如果需要忽略不需要基類型的內(nèi)容的話,干脆我們直接加個(gè) new 來覆蓋掉原本的 ShowDetails。

new 關(guān)鍵字除了用來實(shí)例化,這里還可以用來表達(dá)“覆蓋成員”,所以這是 new 的第二個(gè)用法。

Part 5 重新審視 static 修飾符對(duì)成員的修飾

要說繼承,我們就得說到靜態(tài)和實(shí)例成員。之前我們一直都是在說實(shí)例成員的基本用法(因?yàn)槔^承會(huì)被自動(dòng)復(fù)制下來提供使用)。那么靜態(tài)的成員是不用實(shí)例化就可以使用的(往往寫成 類名.成員名),因此,追加了 static 的關(guān)鍵字的成員,在繼承這個(gè)機(jī)制下又有什么新鮮玩意兒呢?

你只需要記住唯一的繼承原則就好:所有時(shí)候,繼承都是直接把基類型的所有成員都拿下來用的,不論是不是 private 的;只不過訪問修飾符會(huì)限制你可以訪問和使用的級(jí)別。這句話我覺得你應(yīng)該看得懂,就是說所有成員都會(huì)被復(fù)制下來,這包括字段、屬性、方法等等。不論它用了什么修飾符修飾。唯一體現(xiàn)出差別的地方只是在于訪問修飾符會(huì)限制你可使用的范圍和級(jí)別而已。

那么,static 的成員也會(huì)被繼承下來。

假如我有這么一個(gè)例子。我在上面使用了 static 修飾了一個(gè)專門用來顯示一個(gè)人的基本信息的方法;而下面 Student 里也有相同的成員。我在里面寫上了三個(gè)不同的代碼:

  • Person.OutputInfo(person);

  • Student.OutputInfo(person);

  • OutputInfo(person)。

這三個(gè)寫法到底都是怎么調(diào)用和定位的呢?Person.OutputInfo 方法的調(diào)用會(huì)自動(dòng)去找原本 Person 類里的方法;而 Student.OutputInfo 方法在 Student 里有實(shí)現(xiàn),因此會(huì)定位到 Student 里的這個(gè)方法;而 OutputInfo 是沒有寫類型名的,這暗示應(yīng)該是同一個(gè)類型里的成員才是,因此它和第二種寫法等價(jià)。

Part 6 我可以從多個(gè)類派生嗎?

問題很好。我可能有這么一個(gè)需求,就是我可能需要實(shí)現(xiàn)一個(gè)類,這個(gè)類型想從多個(gè)類的里面去拷貝復(fù)制成員來使用,這樣 C# 是允許的嗎?

很遺憾,并不允許,因?yàn)檫@破壞了面向?qū)ο蟮幕驹瓌t:“是什么”的關(guān)系。像是“車是交通工具”,那你還能想到車還能是別的什么玩意兒嗎?一個(gè)事物只能屬于一個(gè)東西的一個(gè)子類型。

有人說,那不對(duì)啊。就比如說一個(gè)小孩子,ta 肯定屬于“人類”這個(gè)范疇,但是 ta 也屬于“動(dòng)物”(指高級(jí)動(dòng)物)這個(gè)范疇啊。是的,但是你想過沒,“‘小孩子’屬于‘人類’”是對(duì)的說法,但“‘人類’本身就屬于‘高級(jí)動(dòng)物’的一個(gè)子類型”。因此繼承關(guān)系實(shí)際上是這樣的:“小孩子→人類→動(dòng)物”(一條鏈),而并不是“人類←小孩子→動(dòng)物”(兩個(gè)分支)。

世間萬物都有一個(gè)歸屬的關(guān)系,且這個(gè)關(guān)系應(yīng)當(dāng)是唯一的。不論怎么往下派生多個(gè)情況,往上面倒回去,總能找到唯一的一個(gè)“根”,這就是面向?qū)ο蟆?/span>


第 36 講:面向?qū)ο缶幊蹋ò耍豪^承控制的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國家法律
永昌县| 竹北市| 镇安县| 社旗县| 遂平县| 邓州市| 江津市| 贵溪市| 绥化市| 洪湖市| 颍上县| 保定市| 洛川县| 十堰市| 酒泉市| 株洲县| 高台县| 湖北省| 夹江县| 静安区| 黄陵县| 读书| 赤壁市| 磐安县| 伽师县| 罗田县| 曲沃县| 巴南区| 广西| 铜鼓县| 博湖县| 奉新县| 惠安县| 广安市| 视频| 封丘县| 威远县| 公安县| 南丰县| 鄂州市| 邻水|