跳轉至內容

C# 程式設計/繼承

來自華夏公益教科書,開放的書籍,開放的世界

類比解釋

[編輯 | 編輯原始碼]

繼承的好處是什麼?

  1. 它為你節省了很多打字。
  2. 它可以避免你重複自己。

繼承的類比解釋

假設你想建立一個鷹、一隻獵鷹和一隻禿鷲。為了建立這些飛行動物,你注意到這些生物都有以下特點:

  1. 飛行
  2. 繁殖
  3. 進食

假設這三種類型的鳥類以完全相同的方式飛行、繁殖和進食。

如果沒有繼承,你將被迫複製程式碼。即,讓鷹飛行的相同程式碼也將被複制以使禿鷲飛翔。對於程式設計師來說,這是一個公理——他們是一群懶惰的人,不想重複自己——重複幾乎總是件壞事。

請注意,鷹、獵鷹和禿鷲實際上都是鳥類。因此,可以說鳥類通常總是有進食、繁殖和飛行的特徵。所以,使用“繼承”,你可以建立一個通用的“鳥類”原型,它可以進食、繁殖和飛行,然後定義好後,你可以讓所有其他特定的鳥類繼承這些特徵。換句話說,使用原型,你可以根據這個原型設計其他特定的鳥類。

這意味著獵鷹會自動知道如何飛行,因為它從通用“鳥類”類繼承了這種行為。你基本上不必重複自己。

繼承是能夠從另一個類(“父”類)建立類,擴充套件父類在派生類或“子”類中的功能和狀態的能力。它允許派生類過載來自其父類的函式。

繼承是面向物件程式設計的支柱之一。它是從另一個類設計一個類的機制,是程式碼可重用的理念之一,支援層次分類的概念。C# 程式由類組成,其中新的類可以從頭開始建立,也可以使用現有類的某些或所有屬性來建立。

與繼承和程式碼可重用性相關的另一個特性是多型性,它允許對不同資料型別進行不同操作時使用相同的函式名稱。因此,C# 透過這兩個特性支援程式碼可重用性。

繼承的重要特徵包括:

  1. 派生類擴充套件其基類。也就是說,它包含其父類的函式和資料,並且它還可以包含自己的資料成員和函式。
  2. 派生類不能更改繼承成員的定義。
  3. 建構函式和解構函式不會被繼承。基類的所有其他成員都會被繼承。
  4. 派生類中成員的可訪問性取決於其在基類中宣告的可訪問性。
  5. 派生類可以重寫繼承的成員。

繼承的示例

using System;
using System.Text;

namespace ContainmentInheritance
{
    class Room
    {
        public int length;
        public int width;
        public int height;

        public Room(int l, int w, int h)
        {
            length = l;
            width = w;
            height = h;
        }
    }

    class Home
    {
        int numberOfRooms;
        int plotSize;
        string locality;
        string name;

        // create an object of class Room inside class Home
        Room studyRoom = new Room(10, 12, 12);

        public Home()
        {
            numberOfRooms = 1;
            plotSize = 1000;
            locality = "Versova";
            name = "study room";
        }

        public void Display()
        {
            Console.WriteLine("MyHome has {0} rooms", numberOfRooms);
            Console.WriteLine("Plot size is {0}", plotSize);
            Console.WriteLine("Locality is {0}", locality);

            int area = studyRoom.length*studyRoom.width;
            Console.WriteLine("Area of the {0} room is {1}", name, area);
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            Home myhome = new Home();
            myhome.Display();

            Console.ReadLine();
        }
    }
}

子型別繼承

[編輯 | 編輯原始碼]

下面的程式碼示例展示了兩個類,EmployeeExecutiveEmployee 具有 GetPayCheckWork 函式。

我們希望 Executive 類擁有相同的函式,但實現方式不同,並且有一個額外的函式,AdministerEmployee

以下是第一個要派生的類的建立。

public class Employee
{
    // We declare one method virtual so that the Executive class can
    // override it.
    public virtual void GetPayCheck()
    {
        // Get paycheck logic here.
    }

    //Employee's and Executives both work, so no virtual here needed.
    public void Work()
    {
        // Do work logic here.
    }
}

現在,我們建立一個 Executive 類,它將覆蓋 GetPayCheck 函式

public class Executive : Employee
{
    // The override keyword indicates we want new logic behind the GetPayCheck method.
    public override void GetPayCheck()
    {
        // New getpaycheck logic here.
    }

    // The extra method is implemented.
    public void AdministerEmployee()
    {
        // Manage employee logic here
    }
}

你會注意到,Executive 類中沒有 Work 函式,因為它繼承自 Employee

static void Main(string[] args)
{
    Employee emp = new Employee();
    Executive exec = new Executive();

    emp.Work();
    exec.Work();
    emp.GetPayCheck();
    exec.GetPayCheck();
    exec.AdministerEmployee();
}

虛方法

[編輯 | 編輯原始碼]

如果基類包含一個虛方法,它在其他地方呼叫該方法,並且派生類覆蓋了該虛方法,那麼基類實際上會呼叫派生類的函式。

public class Resource : IDisposable
{
    private bool _isClosed = false;    // good programming practice initialise, although default
    
    protected virtual void Close()
    {
        Console.WriteLine("Base resource closer called!");
    }
    
    ~Resource()
    {
        Dispose();
    }
    
    public void Dispose()
    {
        if (!_isClosed)
        {
            Console.WriteLine("Disposing resource and calling the Close() method...");
            _isClosed = true;
            Close();
        }
    }
}

public class AnotherTypeOfResource : Resource
{
    protected override void Close()
    {
        Console.WriteLine("Another type of resource closer called!");
    }
}

public class VirtualMethodDemo
{
    public static void Main()
    {
        Resource res = new Resource();
        AnotherTypeOfResource res2 = new AnotherTypeOfResource();
        
        res.Dispose();  // Resource.Close() will be called.
        res2.Dispose(); // Even though Dispose() is part of the Resource class, 
                        // the Resource class will call AnotherTypeOfResource.Close()!
    }
}

建構函式

[編輯 | 編輯原始碼]

派生類不會自動繼承基類的建構函式,並且它無法例項化,除非它提供自己的建構函式。派生類必須使用 base 關鍵字呼叫其基類的建構函式之一。

public class MyBaseClass
{
    public MyBaseClass(string text)
    {
        console.WriteLine(text);
    }
}

public class MyDerivedClass : MyBaseClass
{
    public MyDerivedClass(int number)
        : base(number.ToString())
    { }
    
    public MyDerivedClass(string text) // even though this is exactly the same as MyBaseClass'  
    // only constructor, this is still necessary as constructors are not inherited.
        : base(text)
    { }
}

繼承關鍵字

[編輯 | 編輯原始碼]

C# 從另一個類繼承的語法方法是使用 : 運算子。

示例

public class Executive : Employee

要指示可以重寫的函式,請使用 virtual 標記函式。

public virtual void Write(string text)
{
    System.Console.WriteLine("Text:{0}", text);
}

要覆蓋一個函式,請使用 override 關鍵字

public override void Write(string  text)
{
    System.Console.WriteLine(text);
}

派生函式中缺少 newoverride 關鍵字可能會導致編譯期間出現錯誤或警告:[1] 以下是一個示例

abstract class ShapesA
{
    abstract public int Area(); // abstract!
}

class Square : ShapesA
{
    int x, y;

    public int Area() // Error: missing 'override' or 'new'
    {
        return x * y;
    }
} 

class Shapes
{
    virtual public int Area() { return 0; } // it is virtual now!
}

class Square : Shapes
{
    int x, y;

    public int Area() // no explicit 'override' or 'new' required
    { return x * y; }
}

Square 類函式 Area() 將導致編譯錯誤,如果它從 ShapesA 類派生

error CS0534: 'ConsoleApplication3.Square' does not implement inherited abstract member
'ConsoleApplication3.Shapes.Area()'

如果從普通的 Shapes 類派生,則相同的函式會導致編譯警告

warning CS0114: 'ConsoleApplication3.Square.Area()' hides inherited member 'ConsoleApplication3.Shapes.Area()'.
To make the current member override that implementation, add the override keyword. Otherwise add the new
keyword.

參考資料

[編輯 | 編輯原始碼]
  1. Greg Beech (2010-03-09). "C# 設計:為什麼抽象方法需要 new/override,而虛擬方法不需要? / 答案". http://efreedom.com/: eFreedom. 檢索於 2011-08-11. 使用 .NET 3.5 SP1 中附帶的 C# 3.0 編譯器或 .NET 4.0 中附帶的 C# 4.0 編譯器,我都會在你的第一個例子中收到以下錯誤:[...],在第二個例子中收到以下警告:[...]。在第一個例子中,這是一個錯誤,因為你實際上並沒有重寫基方法,這意味著在具體類中沒有抽象方法的實現。在第二個例子中,這是一個警告,因為程式碼在技術上是正確的,但編譯器懷疑這不是你想要的。這是通常啟用“將警告視為錯誤”編譯設定的一個原因。 {{cite web}}: 外部連結在 |location= (幫助)

華夏公益教科書