程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 論C#之多繼承

論C#之多繼承

編輯:C#入門知識

C#多繼承的討論似乎是個古老的問題了,但今天本文要向大家展示的C#多繼承可能是大家聞所未聞見所未見的,甚至是發明C#語言的人也不曾想到我會這樣去寫代碼,並且自得其樂。       說起多繼承,首先大家可以想想這個問題:你知道在C#中怎麼實現多繼承嗎?       主流的答案無非2種。       答案一:用接口啊,一個類可以繼承自多個接口的。   答案二:C#不支持多繼承,C++才支持多繼承,多繼承會讓代碼變得很亂,因此微軟在設計C#的時候放棄了多繼承。       能夠知道答案二的人顯然懂的更多,我也在很長一段時間內相信C#不支持多繼承,直到2013年5月的一個項目中,我偶然的發現自己的代碼就完全實現了真正意義的多繼承。       先說說什麼是真正意義的多繼承。真正的多繼承應該是像C++那樣的,而不是說像在C#裡面一個類繼承了多個接口就叫多繼承。在C#中,如果一個類實現了多個接口,那麼要為每個接口寫實現,如果接口被多個類繼承,那麼就會有重復的代碼,這顯然是無法接受的。       然而C++那樣的多繼承也確確實實給編碼帶來了很大的麻煩,我也相信微軟真的是因為意識到了多繼承的不合理之處才在C#中擯棄了這個特性。而我在C#中實現的多繼承,第一是真正的多繼承,第二代碼寫的很合理。       請看案例     假如你有一個類叫老虎,還有一個類叫蒼蠅。現在你想新創一個超級老虎類,一種可以飛的老虎。在C++中,你可以定義一種超級老虎類,讓其繼承自老虎和蒼蠅,這樣這種老虎就可以飛了。然而,問題出現了,這種超級老虎由於同時也繼承自蒼蠅,而蒼蠅下面有個方法叫吃,參數類型是屎。吃屎的這個方法顯然跟我們的超級老虎太不搭了。       雖然這個例子有些誇張,但是很多C++程序員真的就是這樣在設計代碼。由於子類繼承了多個父類,而多個父類肯定有些成員跟這個子類不搭調,於是子類的調用者就很難受了。比如上面這個例子,當調用者拿到超級老虎的一個實例時,發現超級老虎下面怎麼會有個吃屎的方法呢!!!真的是要笑死人了。       C++要這樣允許多繼承就必然會造成這個問題。C#程序員就絕對不會寫出這樣滑稽的代碼。對於C#程序員,肯定是要把這個飛的方法提成接口的,然後讓蒼蠅類和超級老虎類都繼承自這個接口。這樣,蒼蠅會飛,超級老虎也會飛。是不是完美解決這個問題?       問題看上去解決了,但是,假如我跟你說蒼蠅飛的方法跟超級老虎飛的方法需要一模一樣:首先張開雙翅,身體前傾,拍打雙翅,起飛,繼續拍打。我們肯定不能把同一份代碼copy一份吧,那是屬於入門級程序員干的事,我們現在已經沒資格干那事了。那怎麼辦呢?簡單快速的做法是使用靜態方法,比如FlyHelper.Fly(...)。       靜態方法解決了代碼重用的問題,但寫起來始終覺得哪裡不對勁。我的超級老虎類和蒼蠅都明明繼承了飛了啊, 為什麼還要這樣調用一句靜態方法。如果以後哪天我想讓我的豬也能飛起來,那豈不是還要來調用這個靜態方法。       到底怎樣才能在C#中實現像C++那樣優雅的繼承呢?        答案揭曉 答案其實很簡單,那就是給IFly接口寫擴展方法。       首先請看這個空接口的定義,及其擴展方法(注意泛型限制):   復制代碼  1 public interface I飛  2 {  3           4 }  5   6 public static class 飛接口的擴展  7 {  8     public static void 飛<T>(this T 飛實例) where T : I飛  9     { 10         Console.WriteLine("准備"); 11         Console.WriteLine("張開雙翅"); 12         Console.WriteLine("起飛"); 13         Console.WriteLine("我飛,我飛,我飛飛飛"); 14     } 15 } 復制代碼     再看老虎和蒼蠅的實現:   復制代碼  1 public class 老虎  2 {  3     public virtual void 自我介紹()  4     {  5         Console.WriteLine("大家好,我是老虎。");  6     }  7 }  8   9 public class 蒼蠅 : I飛 10 { 11     public void 飛一個看看() 12     { 13         this.飛(); 14     } 15 } 復制代碼     再看超級老虎的實現:   復制代碼  1 public class 超級老虎 : 老虎, I飛  2 {  3     public override void 自我介紹()  4     {  5         Console.WriteLine("大家好,我是超級老虎哦!");  6     }  7   8     public void 我會飛喲()  9     { 10         this.飛(); 11     } 12 } 復制代碼     怎麼樣,你看明白了嗎?這個實現是不是很簡單呢?好處是不是大大的有呢?       當以後哪天老板讓你實現一個會飛的超級豬的話,你只需要讓你的超級豬繼承 “I飛” 接口就行了。當哪天老板又不想要這個超級豬飛的話,你也只需要將這個接口繼承刪掉而已。如果你正在開發一個動物王國程序,你可以將飛的功能注入到任何一種動物身上。想想是不是都覺得很爽。       實戰經驗 在實際開發中有沒有使用多繼承的情況呢?我在這裡跟大家分享一個我們項目中大量用到的情景。       我們的項目是基於ASP.NET MVC4的,下面有多個area,可以理解為獨立模塊。每個模塊都有一個相似的注銷登錄的功能,於是我們使用了ILogoutController,然後在其擴展方法中實現了Logout的功能。注銷登錄會做這些事:清空session,保存用戶狀態,寫入日志等。這樣,只要某個controller繼承自ILogoutController,那麼這個controller就擁有了注銷登錄的功能。       原理上講,只要是功能性的相似,我們都可以用空接口擴展的方法來寫多繼承,從而實現功能注入,而注入式的代碼也更容易維護。       總結 最後,再讓我們回顧一下之前用C++寫的超級老虎吃屎的變態例子。這實際上不是C++的錯,而是程序員用錯了多繼承。雖然在語法上C++沒有限制程序員怎麼去寫多繼承,但是從上面的例子分析來看,我們很容得出這樣一個結論:       當需要寫多繼承的時候,被繼承的父類只能是一個功能,而不應是一個完整的類。       如果按照這個思路,那麼今天的這個例子在C++中就可以這樣寫,首先提一個Flyable的類出來,然後讓超級老虎和蒼蠅都繼承這個Flyable。       在C#中,雖然實現多繼承的代碼稍微繞了個彎,但是多繼承帶來的好處是非常明顯的:對不同的類實現注入式的功能,讓你的代碼更符合面向對象的思想。        EDIT: 回復大家的評論     沒想到短短一天時間,這篇帖子收獲了30個評論,看來大家對語言的討論是非常的感興趣啊。感謝大家的熱情參與,但是令我意外的是這篇帖子也收到了9個反對,從評論內容來看,大約60%的人反對本文的觀點。       但是我想說,大家真的沒有必要反對,接受這個思想對你的項目只會有幫助而不會有害處。不管這個思想是否符合多繼承的定義,也不管是否能達到繼承一樣的效果(多態、私有成員),不管你叫它為設計模式,還是語法糖,或者不倫不類的多繼承,這個思想是完全符合面向對象思想的,比用靜態方法要強得多。    

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved