共用方式為


繼承 - 衍生類型以建立更特製化的行為

繼承與封裝和多型一起,是面向對象程序設計三個主要特性之一。 繼承可讓您建立可重複使用、擴充和修改其他類別中所定義行為的新類別。 繼承成員的 類別稱為基類,而繼承這些成員的類別稱為 衍生類別。 衍生類別只能有一個直接基類。 不過,繼承是可轉移的。 如果 ClassC 衍生自 ClassB,且 ClassB 衍生自 ClassAClassC則會繼承 和 ClassBClassA宣告的成員。

備註

結構不支援繼承,但可以實作介面。

從概念上講,衍生類別是基類的特製化。 例如,如果您有基類 Animal,您可能有一個名為 的衍生類別 Mammal ,而另一個名為的衍生類別 ReptileMammalAnimalReptileAnimal,但每個衍生類別都代表基類的不同特製化。

介面宣告可以定義其成員的預設實作。 這些實作會由衍生介面繼承,以及實作這些介面的類別。 如需預設介面方法的詳細資訊,請參閱 介面一文。

當您定義衍生自另一個類別的類別時,衍生類別會隱含地取得基類的所有成員,但其建構函式和完成項除外。 衍生類別會重複使用基類中的程序代碼,而不需要重新實作程序代碼。 您可以在衍生類別中新增更多成員。 衍生類別會擴充基類的功能。

下圖顯示類別 WorkItem ,代表某些商務程式中的工作專案。 和所有類別一樣,它會衍生自 System.Object 並繼承其所有方法。 WorkItem 新增來自其自身的六個成員。 這些成員包含了建構函式,因為建構函式不會被繼承。 類別 ChangeRequest 繼承自 WorkItem 並代表特定類型的工作專案。 ChangeRequest 將另外兩個成員新增至繼承自 WorkItemObject 的成員中。 它必須新增自己的建構函式,而且也會新增 originalItemID。 屬性 originalItemID 可讓 ChangeRequest 實例與套用變更要求的原始 WorkItem 專案相關聯。

顯示類別繼承的圖表

下列範例示範上圖中所示範的類別關聯性如何以 C# 表示。 此範例也會示範如何 WorkItem 覆寫虛擬方法 Object.ToString,以及類別如何 ChangeRequest 繼承 WorkItem 方法的實作。 第一個區塊會定義類別:

// WorkItem implicitly inherits from the Object class.
public class WorkItem
{
    // Static field currentID stores the job ID of the last WorkItem that
    // has been created.
    private static int currentID;

    //Properties.
    protected int ID { get; set; }
    protected string Title { get; set; }
    protected string Description { get; set; }
    protected TimeSpan jobLength { get; set; }

    // Default constructor. If a derived class does not invoke a base-
    // class constructor explicitly, the default constructor is called
    // implicitly.
    public WorkItem()
    {
        ID = 0;
        Title = "Default title";
        Description = "Default description.";
        jobLength = new TimeSpan();
    }

    // Instance constructor that has three parameters.
    public WorkItem(string title, string desc, TimeSpan joblen)
    {
        ID = GetNextID();
        Title = title;
        Description = desc;
        jobLength = joblen;
    }

    // Static constructor to initialize the static member, currentID. This
    // constructor is called one time, automatically, before any instance
    // of WorkItem or ChangeRequest is created, or currentID is referenced.
    static WorkItem() => currentID = 0;

    // currentID is a static field. It is incremented each time a new
    // instance of WorkItem is created.
    protected int GetNextID() => ++currentID;

    // Method Update enables you to update the title and job length of an
    // existing WorkItem object.
    public void Update(string title, TimeSpan joblen)
    {
        this.Title = title;
        this.jobLength = joblen;
    }

    // Virtual method override of the ToString method that is inherited
    // from System.Object.
    public override string ToString() =>
        $"{this.ID} - {this.Title}";
}

// ChangeRequest derives from WorkItem and adds a property (originalItemID)
// and two constructors.
public class ChangeRequest : WorkItem
{
    protected int originalItemID { get; set; }

    // Constructors. Because neither constructor calls a base-class
    // constructor explicitly, the default constructor in the base class
    // is called implicitly. The base class must contain a default
    // constructor.

    // Default constructor for the derived class.
    public ChangeRequest() { }

    // Instance constructor that has four parameters.
    public ChangeRequest(string title, string desc, TimeSpan jobLen,
                         int originalID)
    {
        // The following properties and the GetNexID method are inherited
        // from WorkItem.
        this.ID = GetNextID();
        this.Title = title;
        this.Description = desc;
        this.jobLength = jobLen;

        // Property originalItemID is a member of ChangeRequest, but not
        // of WorkItem.
        this.originalItemID = originalID;
    }
}

下一個區塊示範如何使用基底和衍生類別:

// Create an instance of WorkItem by using the constructor in the
// base class that takes three arguments.
WorkItem item = new("Fix Bugs",
            "Fix all bugs in my code branch",
            new TimeSpan(3, 4, 0, 0));

// Create an instance of ChangeRequest by using the constructor in
// the derived class that takes four arguments.
ChangeRequest change = new("Change Base Class Design",
                  "Add members to the class",
                  new TimeSpan(4, 0, 0),
                  1);

// Use the ToString method defined in WorkItem.
Console.WriteLine(item.ToString());

// Use the inherited Update method to change the title of the
// ChangeRequest object.
change.Update("Change the Design of the Base Class",
    new TimeSpan(4, 0, 0));

// ChangeRequest inherits WorkItem's override of ToString.
Console.WriteLine(change.ToString());
/* Output:
    1 - Fix Bugs
    2 - Change the Design of the Base Class
*/

抽象和虛擬方法

當基類將方法宣告為 virtual時,衍生類別可以使用 override 自己的實作來取得方法。 如果基類將成員宣告為 abstract,則必須在直接繼承自該類別的任何非抽象類別中覆寫該方法。 如果衍生類別本身是抽象的,它會繼承抽象成員,而不實作它們。 抽象和虛擬成員是多型的基礎,這是面向物件程序設計的第二個主要特性。 如需詳細資訊,請參閱 多型

抽象基類

如果您想要使用 new 運算符來防止直接具現化,您可以將類別宣告為抽象。 只有當新的類別衍生自抽象類時,才能使用抽象類。 抽象類可以包含一個或多個被宣告為抽象的方法定義。 這些簽章會指定參數和傳回值,但沒有實作(方法主體)。 抽象類不需要包含抽象成員;不過,如果類別包含抽象成員,則必須將類別本身宣告為抽象。 非抽象本身的衍生類別必須提供抽象基類中任何抽象方法的實作。

介面

介面是定義一組成員的參考型別。 實作該介面的所有類別和結構都必須實作該組成員。 介面可能會定義任何或所有這些成員的預設實作。 類別可以實作多個介面,即使它只能衍生自單一直接基類也一樣。

介面可用來定義不一定具有「是」關聯性的類別的特定功能。 例如,介面可由任何類別或結構實作, System.IEquatable<T> 以判斷型別的兩個物件是否相等(不過類型定義等價)。 IEquatable<T> 並不意味著與基類和衍生類之間存在的同類型「是」關係相同(例如,MammalAnimal)。 如需詳細資訊,請參閱 介面

防止進一步衍生

類別可以宣告自己或其成員為 sealed,以防止其他類別繼承自它或其任何成員。

衍生類別對基類成員的隱藏

衍生類別可以宣告具有相同名稱和簽章的成員,以隱藏基類成員。 new修飾詞可以用來明確地指出該成員不應被視為基底成員的覆寫。 不需要使用 new,但如果不使用 new,會產生編譯器警告。 如需詳細資訊,請參閱 使用 Override 和 New 關鍵詞的版本設定知道何時使用 Override 和 New 關鍵詞