繼承是OOP的一個方面,可以促進代碼重用,更具體地說,代碼重用歸為兩類,經典繼承(‘is-a’關系)和包含/委托模型(‘has-a’關系)。is-a關系:類之間的is-a關系也就是在兩個或兩個以上類類型之間創建了依賴關系,經典繼承的基本思想是新類可以利用既有類的功能。定義一個基類A如下:class A{ PRivate int num; public int Num { get{return num;} set{num = value;} } public A() { Console.WriteLine(Num); } public A(int aaa) {Console.WriteLine("this is A");}}
C#提供了另外一個關鍵字sealed來防止發生繼承。如果我們將類標記為sealed,編譯器將不允許我們從這個類型派生。例如,我們將B改成下面形式:sealed class B:A{}如果C類想要繼承B類的話,將會產生編譯錯誤。class C:B//錯誤,不能擴展sealed標記的類。(string類是個密封的類,不能擴展。){}說明:C#結構總是隱式密封的,因此,我們永遠不可以從結構繼承結構,從類繼承結構或者從結構繼承類。結構只能用于建模獨立的、用戶自定義的數據類型。如果希望使用is-a關系,必須使用類。
OOP的第二個支柱:繼承
1、使用base關鍵字來控制基類的創建。
C#下,一般基本的默認構造函數會在派生類構造函數執行之前被自動調用。為了幫助優化派生類的創建,最好實現子類構造函數,來顯示調用合適的自定義基類構造函數,而不是默認構造函數。class B:A{public B(int a ):base(int aaa)}說明一點,當我們為類定義增加了自定義的構造函數,默認構造函數就自動移除了。因此必要情況下,我們需要為子類重新定義默認構造函數。
2、家族的秘密:protected關鍵字。
我們已經知道,公共項可以在任何地方直接訪問,而私有項除了定義它的類之外,不能從其他對象進行訪問。C#提供了另外一個定義成員可訪問性的關鍵字:protected。當基類定義了受保護數據或受保護成員時,它就創建了一組可以被任何后代訪問的項。如A類增加如下定義,B類就可以訪問這些信息。class A{protected int rC;}這樣的好處,派生類不再需要使用公共方法或屬性來間接訪問數據了。壞處,如果派生類有權直接訪問其父類內部數據,有可能會偶然繞過公共屬性內設置的既有業務規則。最后要注意,對對象用戶來說,受保護數據可以認為是私有的。因此,如果B b = new B();b.rc=10;就會產生錯誤。
我們創建一個類C,和A類的邏輯表示為A類包含C類。如果我們更新A類則就如下:class A{ protected C c= new C();}然而,如果我們要公開被包含對象的功能給外部世界,就需要委托。簡單地說,委托就是增加公共成員到包含類,以便使用被包含對象的功能。例如C類如下:class C{ public void PrintC(){Console.WriteLine("this is C");}}A類如果想要使用C類的PrintC函數,需要增加如下定義class A{ public void GetPrintC() {return c.PrintC();}}嵌套類型定義:這就是‘has-a’關系的另一種說法。在C#中我們可以直接在類或結構作用域定義類型(枚舉、類、接口、結構或委托),這樣做,被嵌套類型會被認為是嵌套類的成員,嵌套類型可以操作其他成員一樣來操作嵌套類型(可以訪問其中的私有成員):public Class D{ public class C{}//公共嵌套類型可以被任何人使用 private class C{}//私有嵌套類型只可以被包含類的成員使用。}
OOP的第三個支柱:C#的多態支持
1、virtual 和override關鍵字。
多態為子類提供了一種方式,使其可以定義由基類定義的方法,這種過程叫做方法重寫。如果基類希望定義可以由子類重寫的方法,就必須使用virtual關鍵字標志方法:partial class A{ public virtual void vA(int aaaaa){//操作邏輯}}如果子類希望改變虛方法的實現細節,就必須使用override關鍵字。class B:A{public override void vA(int aaaaa){//操作邏輯}}注意,重寫方法完全可以通過base關鍵字來自由的使用默認行為,這樣我們就不需要完全重寫實現vA方法的邏輯。public override void vA(int aaaa){base.vA(aaaaa);//其余的處理邏輯}
由于許多基類都是比較模糊的實體,好的設計師會防止在代碼中直接創建基類的對象(實例)。在C#中我們可以使用abstract關鍵字來強制這種編程方式,因此創建一個抽象基類:abstract partial class A{...}此時,A a= new A();就會產生錯誤。盡管我們不能直接創建抽象類,但在創建其派生類時,仍然會在內存中對其進行組裝。因此對抽象類來說,定義若干在分配派生類時間接調用的構造函數是很正常的。