備忘:c#接口與抽象類


今年打算學習unity3d.該引擎支持c#,js,boo(似乎是python相關的一個東東),除了python算是熟悉,其他都是陌生.一番搜索,大量插件是基於c#的,較大的項目也都是基於c#.敲定.

迅速找了本<c#入門經典>,還不錯,詳細,就是講的比較拖節奏.

過程式編程范型語法方面跟c++幾乎一樣,除了沒有指針.

嗯,看到main入口函數也是放在類中,小震精了下.果然程序員都有強迫症呢,夠極端.不過你也不得不是static的啊.

因為后來者,且比c++專注,多了一些方便的語法.類似out,ref,params等關鍵字.但行為和c++類似概念也不差多少.

看到類的時候,在c#的接口及抽象類卡了下.

初看c#的類與c++區別不大.protect,public,重載,虛函數(接口方法或抽象類方法),成員變量(屬性).構造函數,析構函數,static.

區別也有,類似c#不可多繼承,有靜態構造函數.base,this關鍵字等.

剛開始看接口,還以為就是c++中的抽象接口類.接口中只有方法和屬性.

(小記下屬性:其實就是"C#屬性是對類中的字段(fields)的保護,像訪問字段一樣來訪問屬性。同時也就封裝了類的內部數據".所以就是c#幫我們定義的類似getXXX(),setXXX()隱含成員函數,外部類可以直接通過域名符號[.]來獲取操作類內字段.目前未感覺到其魅力.感覺就像是"以面向對象的名義,我要封裝你."這么個強買強賣的意思.)

接口不能包含實現,不可實例化.其方法都要被繼承類實現.這個和c++的抽象類一個意思.但是強對象語義的c++抽象類不會在類中定義成員變量.如果不那么強迫症,弱對象語義的c++抽象類可以定義成員變量.c#接口中的屬性其實並沒有真正在接口中定義字段(成員變量),而只是定義其操作方法(get,set).所以目前為止c#接口與c++強的抽象類一個意思.

 

正當我志得意滿的時候,作者又介紹了c#抽象類.坑爹了.幸好之前有看過com本質論等書籍,一些概念這時對理解起了作用.這次重新喚起了記憶,加深了理解.

相同處:

抽象類不能實例化.抽象類有抽象方法,自身不能實現,必須由繼承類實現.

重要不同處:

抽象類只可以被單繼承,接口可以被多繼承.

抽象類可以包含字段.個人覺得這個是比較其與接口不同之處的很重要的着眼點.說明抽象類在類里,在內存位置上包含了一些具體的內容,而不是"虛"的了.

面向對象對類的比喻:類的成員變量,就是要模擬的物體的特征,而類的成員函數,就是要模擬的物體的行為.

於是對於2個實體,我們從抽象層面上進行對比,對它們的某一組特征和某一組行為,有4種組合:

1 特征相同/行為相同,

2 特征不同/行為不同,

3 特征相同/行為不同,

4 特征不同/行為相同.

很明顯第1和第2點不需要我們探究了.

對於第3點,想象現實中的例子,比如中國人和美國人.這2個人種都有相似的生物學構造,因此行為也多有相似之處.但也有一些(一組)行為不一.如同樣說話,我們說中文,他們說英語.

對於第4點,我們拿人和機器人比較.2者構造肯定不一.但都能跑,能跳,能說.

 

抽象類和接口,對於一個程序員來講,都可以作為表達第3和第4個組合的工具.只是適合不適合的問題. 

假設我們就是那全知全能的程序員(不要不承認,我們都以為自己是上帝).

對第3點組合,我們可以創建一個人的模子(抽象類),把泥土(相同的特征材質)壓進去,然后取出來,為了世界多姿多彩,每個模子的東西取出來后,我們要吹幾口氣(行為接口),一口氣代表一種行為.接着我們做另外的模子,比如老虎,重復上邊的工序.(整個時長大概要6天吧.)我們吹氣的過程比較累人.這就是抽象類創建世界的過程.

我們可以換另外一種方式,因為我們是全知全能的,所以我們可以一次吹出許多不同的氣(但不能換氣),在此期間,我們創造所有需要這些氣的東西.比如我們吹幽默,強壯2口氣,在這口氣結束前,我們創造所有符合這2者的人包括動物等其他東西.在這里,我們的手比較累人,因為沒有模子,每次我們都要重新填材料,捏造型.這是接口的創建世界過程.

對於第3點組合來說,使用抽象類的方法比較直觀,合邏輯.類似的,對於第4點組合,則使用接口的方法比較直觀,合邏輯(因為特征不同,本來就沒有模子).

接口表達的是"can-do"關系,抽象類表達的是"is-a"關系.接口是對世界表象所展現行為的描述.抽象類是對世界實體內在特征聯系的建模.

比如假設另外一個宇宙的程序員想參觀你的創造.於是你提供了一組接口給他操作.他通過接口,看到我們能跑,能飛,能耍嘴皮子.他可以通過接口說,"讓子彈飛一會."於是整個宇宙能飛的都飛起來了.他並不知道飛起來的東西到底是碳基生物,還是硅基生物(他甚至不一定關心構造,上帝們通常只關心物理定律的表現).但他不可以通過接口說,"要有光.".因為這句話包含對這個宇宙屬性的要求,而這是這個宇宙的全知全能者(你)才能干的事.

 

結合一點c++和com接口的經驗類比,接口與抽象類,都想表達和描述一種實體間多態的關系.

對於c#接口,類似於微軟的com接口概念,關注的是異質實體之間的關系.com接口甚至可以達到二進制層面的多態.

比如一旦實現相同的接口,理想世界里不管你是用c++,或者java,不管你是windows下,或linux下,我們都可以通過這個接口進行聯系.接口是對表達異質實體多態的一種訴求.

當限制在c#語言層面,接口是某組行為的統一表述.接口甚至希望自己是無關語言的.

而抽象類或者類的概念,是程序里具體的代碼組織關系的一種體現.關注的是同質實體之間的關系.我們提到抽象類,實際上既是被限制在某種語言內(如c#),某段代碼與某段代碼的某種耦合關系(子類與父類).

我們提到不同語言中2個類(或者抽象類)是沒有意義的,因為它們相互之間根本互不理解.編程概念不同,底層的機器碼不同,代碼間的組織關系不同.

c#中的狗跟c++中的狗是風馬牛不相及的2個東西.你能指望這個宇宙的程序員會跟另一個宇宙的程序員有染嗎,他們連孩子都生不了.

抽象類或者說類的概念,是在某種語言環境下,對代碼間的復用和對代碼間的約束這2個耦合關系的一種實現手段.(希望能着重理解這句話,如果你的代碼或者某種語言的代碼能夠很好的處理這2個耦合關系,那是否使用面向對象,是否有類體系根本就是無關緊要的.)

 

因此,接口實際上是比類更基礎,更廣泛的一個概念,因此類(包括抽象類)可以繼承接口.早期語言如c++因為誕生年代較早,語言中沒有內建這一概念,現在軟件基礎世界的一些弊端,也由此而來,比如dll地獄.c#作為后來者,在語言中實現了對這個概念的追求,雖然還是被限制在了語言層面上(但理想的接口基本是不可能的,它需要各種通用標准願意一起支持).

 

看到這里,讀者應該明白接口是獨立於類體系之外的概念.但有位讀者指出我討論接口和抽象類,就說明我根本沒明白接口.顯然他只看了標題,我想可能還需要繼續補充下.

接口可以被多"繼承".我們已經明白接口不是針對實體的內在特征的建模,只是對一組行為的表述和期待.

顯然類可以有接口,但接口和類不是一個范疇,為什么類要繼承接口呢?其實不過是一種表述或語法上的方便罷了.也許我們應該把繼承換成另外的詞,如"約束"等等,表明類有該接口.

既然c#已經實現了類體系編程范式,而接口方法與抽象類成員方法有一些聯系(比如都是函數,都必須被特定實現),接口在語言編譯實現方面跟抽象類肯定是有一些類似的,就c#的作者而言,我覺得他們讓接口是被"繼承"的是一種自然而然的想法.不必又添加類似關鍵字等等各種細節.只要我們明白繼承接口和繼承抽象類概念上不是一個范疇即可.

 

關於com組件更深的介紹可以參考<com本質論>等相關書籍.個人建議讀前面幾章了解來龍去脈,獲得核心觀點其實已經足夠.后邊的太累贅.

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM