Microsoft .NET 中的基類繼承

Microsoft .NET 中的基類繼承
升級到 Microsoft .NET
Paul D. Sheriff
PDSA, Inc.
2001 年 12 月

摘要:本文介紹了繼承,說明了如何繼承基類,並且介紹了 Microsoft .NET 中的實現繼承和接口繼承。

目標

  • 繼承概述
  • 瞭解如何繼承基類
  • 瞭解接口繼承
  • 瞭解實現繼承

前提條件

要徹底理解本文內容,需要滿足以下條件:

  • 瞭解基本編碼
  • 瞭解類及其工作原理,或者閱讀過 Creating Classes in .NET(英文)一文
  • 可以使用 Microsoft® Visual Basic® .NET

目錄

繼承概述

面向對象編程 (OOP) 語言的一個主要功能就是“繼承”。繼承是指這樣一種能力:它可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴展。在 Microsoft® Visual Basic® .NET 發佈之前,Visual Basic 程序員並不具備這種能力。在 Visual Basic .NET 中,您可以繼承 Microsoft .NET 框架中的類,也可以繼承您自己創建的類。在本文中,我們將學習如何使用繼承,並瞭解繼承是如何大大縮短編程時間的。

簡單示例

在您創建的許多類中,您會發現您常常需要與先前創建的類中的屬性和方法相同的屬性和方法。例如,如果有一個名爲 Person 類的基類,該類包含 LastNameFirstName 屬性以及 Print 方法,您會發現對於 Employee 類您也需要這些屬性和方法。您可能還需要其他屬性,例如 EmployeeID 和 Salary。如果從 Person 類(基類)繼承,您可以將這些屬性添加到新的 Employee 類中,並且仍然可以訪問 Person 類中的所有屬性。繼承是指某個類可將其自身定義爲具有某個特定類的所有屬性和方法,然後再通過添加其他屬性和方法對基類的定義進行擴展的能力。

繼承術語

在深入研究這個主題之前,讓我們先來定義幾個術語。通過繼承創建的新類稱爲“子類”,被繼承的類稱爲“基類”、“父類”或“超類”。在某些 OOP 語言中,一個子類可以繼承多個基類。也就是說,如果有一個 Person 類和一個 Car 類,則 Driver 類可以繼承這兩個類的所有屬性和方法。而在 .NET 中,只允許單一繼承,因此每個子類只能有一個基類。

.NET 支持三類繼承:實現繼承、接口繼承和可視繼承。實現繼承是指使用基類的屬性和方法而無需額外編碼的能力;接口繼承是指僅使用屬性和方法的名稱、但是子類必須提供實現的能力;可視繼承是指子窗體(類)使用基窗體(類)的外觀和實現代碼的能力。

在 .NET 中,一個類可以從某個基類繼承而來,而這個基類又可以從另外一個類繼承而來。而且,您可以在一個類中使用一個或多個接口。

使用繼承的原因

繼承可以避免重複編寫相同的代碼,因此十分有用。如果有兩個單獨的類,而每個類都必須實現 FirstNameLastName 屬性,則可能會出現重複代碼。如果要更改某個屬性的實現方式,則需要查找已實現這些屬性的所有類以進行更改。這不僅要耗費大量時間,還增加了不同類中出現錯誤的風險。

在考慮使用繼承時,有一點需要注意,那就是兩個類之間的關係應該是“屬於”關係。例如,Employee 是一個人,Manager 也是一個人,因此這兩個類都可以繼承 Person 類。但是 Leg 類卻不能繼承 Person 類,因爲腿並不是一個人。

覆蓋

從基類中繼承功能時,您可能會發現在基類中編寫的一般方法僅執行繼承類所需的部分功能。要執行所需的全部功能,您可以在新類中覆蓋基類的方法,而無需使用新的名稱創建一個全新的方法。

進行覆蓋時,您可以選擇完全覆蓋基類的方法,也可以在繼承類中編寫代碼來執行某些操作,然後再調用基類的方法。在覆蓋時,請務必仍然使用與原始方法相同的合約(參數和返回類型)。也可以選擇先調用基類的方法,然後在執行完基類的方法後編寫其他代碼。

繼承基類

繼承使您可以在一個類中使用另一個類的全部屬性和方法。您可以使用關鍵字 Inherits 來獲得基類的功能,而無需將代碼從一個類複製並粘貼到另一個類中。

實現繼承

本文將創建一個新類 LineDelim,它將繼承 Creating Classes in .NET(英文)一文中創建的 Line 類的所有功能。之後,本文將通過添加兩個其他屬性和一個方法對 Line 類進行擴展。要添加的第一個屬性是 Delimiter,使用它可以獲得一個分隔符字符,並將其設置到類中。此分隔符將用於將行中的所有空格替換爲分隔符字符。要添加的第二個屬性是 OriginalLine,它將用於在向文本行插入新的分隔符之前保留文本的原始行。要創建的新方法是 ReplaceAll(),它將用於將文本行中的所有空格替換爲分隔符字符。然後我們將學習如何覆蓋 GetWord 方法,以便使用此分隔符(而不是空格)分隔文本行並搜索第一個詞。

構建示例窗體

圖 1 中顯示的示例窗體將用來測試要創建的繼承類。

圖 1:用於測試繼承的示例窗體

  1. 要創建圖 1 所示的窗體,請單擊 Project(項目),然後單擊 Add Windows Form(添加 Windows 窗體)。
  2. 將窗體命名爲 frmLineTest.vb 並單擊 OK(確定)。
  3. 然後在該窗體上創建相應的控件並設置屬性,如表 1 所示。

    表 1:用於測試繼承的窗體

    控件 屬性
    Label Name Label1
      Text Line of Text
    TextBox Name txtLine
      Text The rain in Spain stays mainly in the plain
    TextBox Name txtDelim
      Text ,
    GroupBox Name fraWord
      Text Get First Word
    CommandButton Name btnFirst
      Text Get Word
    TextBox Name txtFirstWord
      Text  
      ReadOnly True
    CommandButton Name btnReplace
      Text Replace
    TextBox Name txtReplace
      Text  
      ReadOnly True

構建 Line 類

接下來將構建要繼承的 Line 類。

  1. 從菜單中單擊 Project(項目),然後單擊 Add Class(添加類)。
  2. 鍵入如下所示的代碼。
    Public Class Line
        Private mstrLine As String
        
        Property Line() As String
            Get
                Return mstrLine
            End Get
            Set(ByVal Value As String)
                mstrLine = Value
            End Set
        End Property
        
        ReadOnly Property Length() As Integer
            Get
                Return mstrLine.Length
            End Get
        End Property
        
        Public Function GetWord() As String
            Dim astrWords() As String
            
            astrWords = mstrLine.Split(" ".ToCharArray())
            
            Return astrWords(0)
        End Function
    End Class
    

創建子類

既然窗體和基類都已經創建完畢,現在便可以開始執行繼承了。

  1. 單擊 Project(項目),然後單擊 Add Class(添加類)。將該類命名爲 LineDelim.vb 並單擊 OK(確定)。
  2. 添加新類時,請修改 Visual Basic .NET 所創建的代碼,使之與下面的示例代碼相似。
    Public Class LineDelim
        Inherits Line
    
    End Class

因爲添加了 Inherits Line 語句,所以您可以在這一新創建的類中使用 Line 類的所有屬性和方法。

試一試

  1. 打開 frmLineTest.vb 窗體。
  2. 雙擊 Get Word(取詞)按鈕。
  3. 向此按鈕的單擊事件過程添加以下代碼:
    Protected Sub btnFirst_Click(ByVal sender As Object, _
     ByVal e As System.EventArgs) Handles btnFirst.Click
        Dim oLine As LineDelim = New LineDelim()
          
        oLine.Line = txtLine.Text
        txtFirstWord.Text = oLine.GetWord()
    End Sub
  4. 運行項目,並在窗體上單擊 Get Word(取詞)按鈕。您將看到“The”一字出現在按鈕旁邊的只讀文本框中。

Inherits 語句的功能非常強大,只需要使用這一個語句,就可以在 LineDelim 類中使用 Line 類的所有屬性和方法。儘管這個新類尚未執行任何新的操作,但它卻表明從 Line 類中繼承的所有代碼都可以正常工作。

添加其他功能

現在,您可以使用其他屬性和方法對 LineDelim 類進行擴展。要向 LineDelim 類添加兩個新的屬性,請執行以下步驟。

  1. 在上一部分添加的 Inherits 語句後添加兩個 Private 變量,如下所示。
    Private mstrDelim As String = " "
    Private mstrOriginal As String
  2. 鍵入如下代碼,爲這兩個 Private 變量添加適當的 Property 語句。您可以將以下代碼放在上面輸入的兩行代碼後面(緊挨這兩行)。
    Public Property Delimiter() As String
        Get
            Return mstrDelim
        End Get
        Set(ByVal Value As String)
            mstrDelim = Value
        End Set
    End Property
       
    Public ReadOnly Property OriginalLine() As String
        Get
            Return mstrOriginal
        End Get
    End Property

現在您可以使用 Delimiter 屬性設置並獲取 Private 變量 mstrDelim 的值。

如果不希望其他人更改這些屬性,您可以將屬性設爲只讀。要執行此操作,請不再使用 Set 語句,並在 Property 語句中添加 ReadOnly 屬性。有關示例,請參見上面代碼中顯示的 OriginalLine 屬性聲明。

接下來,需要創建一個稱爲 ReplaceAll 的方法,此方法可以將文本行中的所有空格替換爲傳遞到 Delimiter 屬性中的分隔符字符。

Public Function ReplaceAll() As String
    mstrOriginal = MyBase.Line
      
    Return MyBase.Line.Replace(" ", mstrDelim.ToChar())
End Function

ReplaceAll 方法通過基類的 Line 方法檢索原始文本行。而以前從基類中檢索屬性時使用的是 MyBase.Line 語法。ReplaceAll 函數將 MyBase.Line 屬性的值放入您剛剛爲該類創建的 Private 變量 mstrOriginal 中。String 數據類型的 Replace 方法將字符串字符的所有實例替換爲在 Delimiter 屬性中設置的新分隔符字符 mstrDelim。

MyBase 關鍵字

可以從任一子類使用 MyBase 關鍵字,以調用基類中的任何屬性或方法。即使基類的方法在子類中已被覆蓋,您也可以使用該關鍵字對其進行調用。例如,如果在基類中存在 ReplaceAll 方法,但在子類中該方法已被覆蓋,您可以從子類的 ReplaceAll 方法中調用基類的 ReplaceAll 方法。

試一試

  1. 打開 frmLineTest.vb 窗體。
  2. 雙擊 Replace(替換)以調出單擊事件過程。
  3. btnReplace 按鈕的單擊事件中編寫以下代碼:
    Protected Sub btnReplace_Click( _ 
     ByVal sender As Object, _
     ByVal e As System.EventArgs) Handles btnReplace.Click
        Dim oLine As LineDelim = New LineDelim()
          
        oLine.Delimiter = txtDelim.Text
        oLine.Line = txtLine.Text
        txtReplace.Text = oLine.ReplaceAll()
    End Sub

    此代碼將 Delimiter 屬性設置爲在示例窗體的 txtDelimiter 文本框中輸入的值。然後您可以調用 ReplaceAll 方法,將文本行中的所有空格更改爲新的分隔符字符。

  4. F5 鍵運行該項目。
  5. 單擊 Replace(替換)。您將看到,在此按鈕旁邊的文本框中,句中的每個詞之間都有一個逗號。

覆蓋方法

添加 Delimiter 屬性後,您可能想更改 LineDelim 類中的 GetWord 方法,以便使用相應的分隔符替代 Line 類使用的單個空格。因爲您不一定想更改基類,所以需要覆蓋 LineDelim 類中 GetWord 方法的功能。在 LineDelim 類中創建新的 GetWord 方法之前,您需要在 Line 類的 GetWord 方法聲明中添加一個關鍵字。

  1. Solution Explorer(解決方案資源管理器)窗口中,打開 Line.vb 類的代碼窗口。
  2. 找到 GetWord 方法的聲明(聲明不包含參數),如下所示:
    Public Overloads Function GetWord() As String
  3. 在函數聲明中添加關鍵字 Overridable,如下所示(沒有此關鍵字,就無法覆蓋此方法)。
    Public Overridable Overloads Function GetWord() As String
  4. 打開 LineDelim.vb 類,並使用如下代碼添加新的 GetWord 方法。
    Public Overloads Overrides Function GetWord() As String
        Dim astrWords() As String
          
        astrWords = MyBase.Line.Split(mstrDelim.ToCharArray())
          
        Return astrWords(0)
    End Function

如果要更改基類中方法的功能,則有必要在函數聲明中添加 Overrides 關鍵字。現在,LineDelim 類中的 GetWord 方法就可以使用 Delimiter 屬性的值來分隔句中的詞。

如果只覆蓋其中一個 GetWord 方法,則代碼只能查看這一個版本的方法,而無法調用其他版本的 GetWord 方法。要顯示所有方法,您必須覆蓋每一個方法,就象您在 LineDelim 類中所執行的操作一樣。

試一試

  1. F5 鍵運行該項目。
  2. 在句中的每個詞之間都輸入一個逗號,並在 Delimiter(分隔符)文本框中輸入一個逗號。
  3. 單擊 Get Word(取詞)。

    句中的第一個詞將出現在該按鈕旁邊的文本框中。

抽象類

在本文上一部分的示例中,我們學習瞭如何創建 Person 對象,這是因爲我們想處理普通的人。但是您可能會發現,如果不先添加一些特定的行爲和/或數據,就無法使用 Person 類執行任何操作。因此您可以將 Person 類變爲抽象類,抽象類僅定義將由子類創建的一般屬性和方法。

將 Person 類定義爲只能被繼承的抽象類,而不是在運行時實際創建的對象。從該類繼承的每個類(如 Employee 類)都將使用特定的功能來創建所有相應的屬性和方法。例如,Employee 類將創建實際的 Print 方法,而 Person 類僅定義必須存在 Print 方法;Person 類中沒有與 Print 方法相關聯的代碼。

使用抽象類的原因有多種。對於強制子類設計人員實現應用程序通常所需的所有接口,抽象類非常有用。您可以在不破壞客戶端應用程序的情況下向子類添加新方法,這是使用接口所無法實現的;可以在基類中提供許多默認實現方法,從而減少子類需要完成的工作量。

接口繼承

創建抽象類時,請使用關鍵字 Interface 而不是 Class。爲接口命名,然後定義需要子類實現的所有屬性和方法。這是因爲基類中沒有可以實現的屬性和方法,它只包含一般數據,而不包含方法。您所創建的只是一個合約,它規定所有使用此接口的子類都必須遵循一定的規則。

  1. 現在,請在已創建的項目中添加一個新類。
  2. 從 Visual Studio 菜單中,單擊 Project(項目),然後單擊 Add Class(添加類)。
  3. 在類中添加以下代碼:
    Interface Person
        Property FirstName() As String
        Property LastName() As String
        
        Sub Print()
        Sub Talk()
    End Interface

    您會發現,您定義屬性和子過程的方法與您通常定義這些屬性和過程的方法一樣。唯一的差別在於,您沒有爲它們編寫任何代碼。現在來看看如何在類定義中使用此接口。

  4. 在上一步驟創建的類文件中添加以下代碼:
    Public Class Employee
        Implements Person
        
        Private mstrFirstName As String
        Private mstrLastName As String
        
        Property FirstName() As String _
         Implements Person.FirstName
            Get
                Return mstrFirstName
            End Get
            Set
                mstrFirstName = Value
            End Set
        End Property
        
        Property LastName() As String _
         Implements Person.LastName
            Get
                Return mstrLastName
            End Get
            Set
                mstrLastName = Value
            End Set
        End Property
        
        Sub Print() Implements Person.Print
            ' 在此處添加一些代碼        
        End Sub
    
        Sub Talk() Implements Person.Talk
            ' 在此處添加一些代碼
        End Sub
    End Class

在 Employee 類定義之後的第一行是 Implements Person。此關鍵字表示您要遵守 Person 接口中定義的合約。現在您可以定義該合約中的所有屬性和方法。在每一個 Property 語句後面,都必須包含 Implements 關鍵字,並且必須指定接口的名稱和您正在使用的方法/屬性的名稱(兩個名稱之間有一個點 [.])。Visual Basic .NET 將跟蹤每一個接口,在所有接口創建完畢之前,您不能編譯應用程序。

如果要運行代碼,則需要創建相應的子過程,因爲在上面的示例中這些子過程被保留爲空。創建所有子過程後,您就可以與您通常創建並使用任何其他對象一樣,聲明並使用新的 Employee 對象了。

選擇要使用的繼承類型

有時候很難決定到底是使用實現繼承還是使用接口繼承,很多情況下,可能兩種繼承都會用到,但都只涉及一小部分。例如,您可能需要在 Line 類中添加必須被子類覆蓋的方法定義,在過程定義中使用 MustOverride 關鍵字即可實現此操作。

Public MustOverride Sub Init()

將此定義添加到類中以後,其作用類似於一個接口。在子類中,必須定義 Init 方法,並且該方法必須使用 Overrides 關鍵字。以下是如何定義 Init 方法的示例:

Public Overrides Sub Init()
   mstrDelim = " "
   mstrLine = "測試行"
End Sub

同樣,請記住使用 Overrides 關鍵字。該關鍵字用於通知編譯器此方法將覆蓋父類中的 Init 方法。

注意: Microsoft .NET 框架的聯機幫助中提供了設計指南,可以幫助您決定要使用的繼承類型。

阻止繼承

在某些情況下,您可能不希望其他類繼承您的類。如果是這樣,您可以使用關鍵字 NotInheritable 來阻止類的繼承。

Public Class NotInheritable Employee
   ' 類定義
End Class

Visual Basic 6.0 以來的新增功能

使用 Visual Basic .NET,您可以繼承 .NET 框架包含的所有類。您可以創建自己的類,使這些類繼承現有的類;並通過對代碼進行簡單更改來添加或刪除功能。

總結

本文介紹瞭如何繼承基類,如何向基類添加其他屬性,以及如何使用 Overrides 關鍵字來替換基類中定義的功能。還介紹了使用 MyBase 關鍵字調用基類中的方法,從而擴展基類的功能。雖然繼承並不是對所有的應用程序都適用,但如果使用正確,繼承將成爲一種非常強大的工具。

關於作者

Paul D. Sheriff 是 PDSA, Inc. 的所有者。該公司位於南加利福尼亞州,是一家自定義軟件開發和諮詢公司。Paul 是南加利福尼亞的 MSDN 區域總監,著有《Paul Sheriff Teaches Visual Basic》一書,他爲 Keystone Learning Systems 製作了 70 多套關於 Visual Basic、SQL Server、.NET 和 Web 開發的視頻教材,最近還將與 Ken Getz 合作出版一本關於 SAMS 的書,書名是《ASP.NET Jumpstart》。有關詳細信息,請訪問 PDSA, Inc. 的 Web 站點 www.pdsa.com。

關於 Informant Communications Group

Informant Communications Group, Inc. 是一家專注於信息技術行業的多媒體公司。它成立於 1990 年,專門從事軟件開發出版物、會議、目錄發佈和 Web 站點等業務。ICG 在美國和英國均設有辦事處,目前已成爲享有盛譽的媒體和營銷內容集成商,並以高質量的技術信息滿足 IT 人員不斷增長的需求。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章