第5章 繼承性、多態性和命名空間編程
• 繼承性(inheritance)和多態性(polymorphism)是面向對象的重要機制,其提升了軟件模塊的可複用性和可擴充性。在第4章中已經介紹了面向對象的另外一特性繼承性。這一章咱們將介紹了兩種C# 2008中的重要概念抽象類和密封類,它們顯示了C#語言的多態性。
• C# 2008程序是經過使用名空間來組織的。其不只能夠是應用程序的內部結構體系,一樣也能夠是外部結構體系。若是程序中的一些元素要被導出到其餘程序,可使用命名空間。程序中免不了會出現錯誤,這時就會用到C#語言的異常處理機制。
5.1 繼承機制
• 面向對象的重要機制之一繼承是可使用之前建造類的方法和屬性。經過簡單的程序代碼來建造功能強大的類,不只會節省不少編程時間,並且還能夠減小代碼出錯的機會。
5.1.1 繼承的概念
• 經過一個實際應用問題,來說解類的繼承這個問題,如代碼所示。
• 【本示例參考:\示例代碼\Chap05\NoUseInheritance】
• 在上面的程序中,定義了Person類和Student類,結果發現Student類包含了Person類的全部屬性和方法。在C# 2008語言中,只要代表Student類繼承了Person類,就不用在類Student中重複寫類Person中的代碼了。
5.1.2 繼承的機制
• 子類對象中的成員變量的初始化實過程以下:
• (1)分配成員變量的存儲空間,並進行默認的初始化。
• (2)綁定構造函數參數,就是把new Person(實際參數列表)中所傳遞進的參數賦值給構造函數中的形式參數變量。
• (3)若是有this()調用,則調用相應的重載構造方法。
• (4)顯式或隱式追溯調用父類的構造方法。
• (5)進行實例變量的顯式初始化操做,也就是執行定義成員變量時就進行賦值的語句。
• (6)執行當前構造函數體中的代碼。
5.2 多態性
• 在面向對象的系統中,多態性容許對一個對象進行操做,由一個對象完成一系列的動做,具體實現哪一個動做由系統負責解釋。在C# 2008中,多態性的定義是:同一操做做用於不一樣的類的實例,不一樣類將進行不一樣的解釋,最後產生不一樣的執行結果。其支持兩種類型的多態性:編譯時多態性和運行時多態性。編譯時多態性是經過重載來實現的,其根據傳遞的參數、返回的類型等信息決定實現何種操做。在第4章已經介紹了。運行時多態性是指直到系統運行時,才根據實際狀況決定實現何種操做,其是經過虛方法來實現的。
5.2.1 虛方法
• 虛方法是經過在方法聲明語句的訪問修飾符和返回類型之間放置virtual關鍵字來實現的。當調用虛方法時,運行將肯定調用對象是什麼類的實例,並調用適當的覆蓋方法,經過override關鍵字來覆蓋。經過一段簡單的代碼如所示,來講明虛方法跟非虛方法的區別。
• 【本示例參考:\示例代碼\Chap05\Difference】
• 運行結果:
• A.F
• B.F
• B.G
• B.G
5.2.2 抽象類和抽象方法
• 抽象方法能夠當作是沒有方法體的虛方法。其是必須被派生類覆蓋的方法。在C# 2008中是經過關鍵字abstract來實現的。若是類的任何一個方法都是抽象的,則該類也必須聲明爲抽象的。
• 抽象類的用途是提供多個派生類可共享的基類的公共定義,並使用abstract關鍵字定義。先定義一個抽象類:
• abstract class MyClass
• {
• }
• 抽象類能夠當作是接口和普通類的結合,代碼5-6演示了抽象方法的使用。
5.2.3 抽象方法
• 抽象類中能夠定義抽象方法,若是一個方法要聲明爲抽象方法,則方法前加上abstract修飾符便可。抽象方法是一個新的虛方法,它不提供具體的方法實現代碼。只能在抽象類中聲明抽象方法,對抽象方法,不能使用static或virtual修飾符,並且方法中不能有任何可執行代碼,只要給出方法的原型就能夠了。抽象類的派生類必須實現全部抽象方法,如代碼所示。
• 【本示例參考:\示例代碼\Chap05\AbstractMethod2】
5.2.4 密封類和密封方法
• 與override關鍵字連用的還有sealed關鍵字,sealed關鍵字用來表示密封的意思。在C# 2008中,密封類的做用是限制擴展性和靈活性。先定義一個簡單的密封類:
• sealed class MyClass
• {
• }
• 當程序中密封了某個類時,其餘類是不能繼承該類的,如代碼所示。
• 【本示例參考:\示例代碼\Chap05\SealedClass1】
5.2.5 方法的隱藏
• 【本節示例參考:\示例代碼\Chap05\HidingMethod】
• 還有一種方法能夠實如今派生類中的覆蓋,即new關鍵字。這種過程叫作方法的隱藏。可是,派生類和非抽象類的基類方法必須有相同的方法。代碼演示瞭如何實現方法的隱藏。
• 運行結果:
• Study方法被調用
• Person方法被調用
• 方法隱藏的做用就是能夠改變基類的方法。若是一個派生類型被強制向上轉換爲基類型,基類型的方法將被調用。
5.3 類型的轉換
• 第3章講述了基本數據類型變量的類型轉換問題,其實對象類型轉換也差很少是一個道理。不過在處理對象以前,有時候還常常對變量的類型進行判斷,因此下面從類型的判斷開始。
5.3.1 is關鍵字
• is關鍵字能夠檢查對象是否與特定的類型兼容。顯然,能夠用它來判斷對象是否爲給定的類型。定義格式爲:
• operand is type
• 在上述定義中,當type是一個類,而operand也是該類型、或繼承了該類型、或封箱到該類型中時結果爲true;當type是一個接口類型,而operand也是該類型,或者執行該接口的類型結果也爲true;當type是一個值類型,而operand也是該類型,或者被拆箱到該類型中時結果也爲true。以下面的一個簡事例:
• If (newClass is object)
• {
• 執行相應的操做;
• }
• else
• {
• 執行相應的操做;
• }
5.3.2 轉換機制
• 值類型和引用類型的根本區別在於在內存中的存儲方式,值類型老是在內存中的棧中存儲,而引用類型倒是在堆棧中存儲。堆棧與棧的區別在於,當定義一個值類型變量時,會在棧中分配適當大小的內存,內存中的這個空間用來存儲變量所含的值。引用變量也利用棧,但這時棧包含的知識對另外一個內存位置的引用,而不是實際值。這個位置是堆中的一個地址。
• 值類型表明基本數據類型,分爲三種簡單類型(基本數據類型)、結構(用戶定義的值類型)和枚舉。而引用類型分爲類、接口、數組和委託。在第2章已經介紹了值類型間的顯示轉換和隱式轉換。這一小節中,將介紹引用類型間的轉換。代碼5-15演示瞭如何實現子類轉化爲父類。
• 【本示例參考:\示例代碼\Chap05\Transformation1】
5.3.3 as關鍵字
• as關鍵字用於在兼容的引用類型之間執行轉換,把一種類型轉換爲指定的引用類型。與強制轉換不一樣as關鍵字並不會引起錯誤。其基本格式爲:
• operand as type
• 當operand的類型是type類型、operand的類型能夠隱式轉換爲type類型、operand的類型能夠封箱到類型type類型時,operand的類型就會被轉換爲相應type類型,不然operand的類型會被賦予null。代碼演示了as關鍵字的使用。
• 注意:若是operand的類型能夠顯示轉換爲type,則operand的類型的結果就是null。
• 【本示例參考:\示例代碼\Chap05\TestAs】
5.3.4 封箱(boxing)和拆箱(unboxing)
• 第2章討論了值類型間的轉換,而上面的小節討論了引用類型間的轉換,同時介紹了二者間的區別。封箱(boxing)和拆箱(unboxing),就是在任何值類型、引用類型和object類型間進行轉換。
• 1.封箱(boxing)
• 封箱是把值類型轉換爲System.Object類型,或者轉換爲由值類型執行的接口類型。當把一個值類型轉換一個System.Object類型,也就是建立一個System.Object類型實例並將這個值複製給這個object。
• 2.拆箱(unboxing)
• 和封箱轉換正好相反,拆箱轉換是將一個對象類型顯示的轉換成一個值類型,或是將一個接口類型顯示的轉換成一個執行該接口的值類型,這種轉換是顯示進行的,其語法相似於前面的顯示類型轉換。拆箱的過程分爲兩步,首先檢查這個對象實例。而後把這個實例的值複製給值類型的變量。
5.4 異常處理
• C# 2008語言支持錯誤處理和異常(exception)。異常處理是捕獲預期的和突發事件的技術,其目的是在錯誤發生以前,可以事先預料錯誤,使程序更可靠。異常定義了程序中遇到的非致命的錯誤,而不是編譯時的語法錯誤。
5.4.1 異常處理基礎
• 異經常使用來表示在應用程序執行期間執行間的錯誤,以及其餘意外行爲。當遇到下列狀況時,可能會引起異常:
• 操做資源不可用。
• 公共語言運行庫遇到意外狀況。
• 自定義拋出異常。
• 在.NET Framework中,類Exception類表示基類異常,其餘異常從其繼承而來。該類經常使用的屬性有StackTrace屬性,InnerException屬性,Message屬性,HelpLink屬性,Data屬性,Source屬性和TargetSite屬性。
5.4.2 異常的捕獲
• 理解了異常就能夠捕獲異常,代碼演示瞭如何實現異常的捕獲。
• 【本示例參考:\示例代碼\Chap05\TestException】
• 代碼異常捕獲:TestException
• using System;
• using System.Collections.Generic;
• using System.Linq;
• using System.Text;
•
• namespace TestException
• {
• class Program
• {
• static void Main(string[] args)
• {
• int[] myArray = new int[4] { 1, 2, 3, 4 };
• //錯誤的賦值
• myArray[5] = 5;
• }
• }
• }
5.4.3 throws關鍵字
• 在異常處理中,還有一個throws關鍵字,其做用也很是重要。爲了詳細的瞭解該關鍵字,請從代碼開始講解。
• 【本節示例參考:\示例代碼\Chap05\TestThrows1】
• 在上述的例子中,假設TestException類和MyException類不是同一我的寫的,寫TestException類的人,在main方法中調用MyException類的數組時候,如何知道數組會出現異常狀況呢?也只有知道了它會發生錯誤,纔可以想到用try……catch語句去處理可能發生的異常。
5.4.4 finally關鍵字
• 【本節示例參考:\示例代碼\Chap05\TestFinally】。
• 在try……catch語句後,還能夠有一個finally語句,finally語句中的代碼塊無論異常是否被捕獲老是要執行的,代碼演示了該關鍵字的使用。
• 運行結果:
• 出現錯誤了
• 程序執行完畢
5.4.5 異常的使用細節
• 當使用異常的時候,必須注意:
• 一個方法被覆蓋時,覆蓋它的方法必須拋出異常或異常的子類。
• 若是父類拋出多個異常,那麼覆蓋方法必須拋出那些異常的一個子集,也就是說,不能拋出新的異常。
5.5 命名空間
• 微軟公司爲程序開發人遠提供了成千上萬的類組成類庫,若是不對這些類進行分門別類的使用和存放,就會像開發人員不用文件夾去管理衆多的文件同樣,在使用的時候不只極度困難和不方便,也極易出現類的命名衝突問題。在C# 2008中是經過引入命令空間機制,提供類的多層類命名空間,來解決上述問題。
5.5.1 聲明命名空間
• 【本節示例參考:\示例代碼\Chap05\Testnamespace】
• 命名空間是C#類、接口、委託、枚舉和其餘類型的一個邏輯上的組合。其不只在使用上靈活了不少,並且還解決了類型之間的命名衝突。命名空間是一種邏輯組合,而不是物理組合。其聲明的基本格式:
• namespace qualified-identifier namespace-body ;
• 在上述聲明中,命名空間使用的關鍵字「namespace」,其後跟名字空間名和名字空間主體,還必須跟分號。
5.5.2 導入命名空間
• 有了命令空間的基本知識,那麼在不一樣命令空間中的類是如何調用的了,請看代碼,【本示例參考:\示例代碼\Chap05\Testnamespace2】。
• 當編譯上述代碼時,會出現錯誤。緣由在於:在Testnamespace類中,直接調用了Test類。編譯器認爲兩個類在同一個命令空間中,即類Test的完整名被認爲是Chap05.Test,因此找不到類Test。只要把「new Test().WriteInfo();」這一句改成「new Chap05.SonPath.Test().WriteInfo();」就能夠經過編譯了。
5.5.3 使用指示符
• 【本節示例參考:\示例代碼\Chap05】
• 使用指示符的目的是爲了方便使用其餘的命名空間中定義的命名空間和類型。其有兩種類型:別名使用指示符和命名空間使用指示符。命名空間使用指示符在上一小節已經介紹。
• 別名使用指示符定義了一個別名,之後可使用這個別名來代替一個類型。這在兩個庫的名字可能發生衝突的狀況下很是有用。別名還能夠避免使用冗長的命名空間。其基本格式以下:
• using MS = MyProgram. MyCSharp. Chap05;
5.6 小結
• 本章接着上一節的內容繼續介紹面向對象,主要內容是面向對象的繼承性、多態性、封裝性,同時還有在編程中常常遇到的類型轉換、異常處理、命令空間。
• 面向對象的三大特徵雖然解釋起來很簡單,可是要融會貫通卻很難,讀者千萬不要被它們的假象所迷惑。後面的三個概念雖然看起來很陌生,可是學起來很是簡單。所以,讀者要把重心放在前三小節中。