解讀經典《C#高級編程》第七版 Page100-107.繼承.Chapter4

前言


本章節繼續講實現繼承。

微信

實現繼承

密封類和密封方法框架

密封類和方法的概念很簡單,就是爲了避免容許類和方法被繼承和擴展。不容許擴展通常的緣由有:ide

  • 若是類或者方法被擴展,可能會致使類庫執行錯誤
  • 由於版權緣由,不容許第三方隨意擴展該類

.Net庫有不少密封類,使用者不能隨意擴展。我猜想這麼作多是由於設計者想保持框架的純淨性和單一性,即不但願使用者隨意擴展而致使出現衆多「分支框架」,最終的致使框架的碎片化(參考Andriod的碎片化)。
最典型的,string類型就是密封類。咱們能夠猜想擴展方法的出現(.Net3.5版本)就是由於對.Net框架密封類有擴展的需求,可是密封類沒法繼承,因此出一個擴展方法做爲「補償辦法」,必定程度上解決了對基礎框架的擴展問題。函數

密封類案例
/// <summary>
/// 密封類案例:不能被繼承,其方法不能被override。這個類同時仍是基類
/// </summary>
public sealed class SaUser
{
    public void Test()
    {
        //密封類方法不能有virtual關鍵字,由於virtual表明可override,這和密封類的概念矛盾了
    }

    /// <summary>
    /// 此寫法爲非法,不一樣經過編譯: 基類中不能定義密封方法
    /// </summary>
    public sealed override void Test2()
    {
        //Test2()不能定義sealed,也不能定義sealed override,都會致使編譯異常。
    }
}

密封方法案例
/// <summary>
/// 普通基類
/// </summary>
public class SaUser2
{
    public virtual void Test()
    {
        //可擴展的方法
    }
}
/// <summary>
/// 派生類的密封方法
/// </summary>
public class SaUserChilid : SaUser2
{
    /// <summary>
    /// 密封方法必須是重寫的方法
    /// </summary>
    public sealed override void Test()
    {
        //密封方法必須同時有sealed和override關鍵字
        base.Test();
    }
}

有趣的現象
如上代碼,有個有趣的現象是,定義密封類只須要使用sealed關鍵字,而定義密封方法時,sealed必須和override配對使用。同時,在基類中也不能對方法實行密封。爲何會這樣呢?我猜測,設計者的觀點應該是:「所謂密封,應該是對一個原先處於「開放」狀態下的類進行密封」。但對密封的這個限制,確實使得語言特性不夠靈活。實際上,對基類的方法進行密封,雖然極少發生,但理論上仍是有需求的。this

派生類的構造函數設計

前面咱們講過了單個類的構造函數。咱們知道構造函數是必須的,當咱們沒有類的構造函數時,系統會默認提供一個構造函數,由於類中字段的數據初始化是依賴於構造函數的。
當咱們還要構造較爲複雜的派生類時,其構造函數如何運行,就成爲一個很是值得研究的問題。研究清楚了它,你才能用「合適」的辦法完成派生類的初始化。派生類的構造函數的麻煩,來自於類的「多構造函數」,也就是構造函數的重載。
首先,咱們要知道構造函數的原則:3d

  1. 每一個類(基類,派生類)的構造函數都是必須的。
  2. 類的構造是自底向上來構造的,先構造基類,再逐級向上構建派生類。(爲何必須這樣逐級構造,能夠看原文章瞭解)
  3. 類的構造方法是可重載的(多構造函數)。

在以上原則下,當一個派生類要開始構造時,咱們發現,關鍵要注意什麼?是構造鏈不能斷!在這點上編譯器會智能判斷,若是它發現構造鏈斷了,會發生編譯錯誤。咱們能夠看個例子,這個例子我之前的文章就貼過。code

/// <summary>
/// 基類
/// </summary>
class Line
{
    private int thick;

    /// <summary>
    /// 基類構造方法:代號A
    /// </summary>
    Line()
    {
        thick = 1;
    }
}

/// <summary>
/// 派生類
/// </summary>
public class Rect
{
    private int width;
    private int height;

    /// <summary>
    /// 構造方法:代號B
    /// </summary>
    /// <param name="length"></param>
    Rect(int length)
        :this(length, length)   //初始化器
    {
        //構造一個正方形
    }

    /// <summary>
    /// 構造方法:代號C
    /// </summary>
    /// <param name="width"></param>
    /// <param name="height"></param>
    Rect(int width, int height)
        :base()     //初始化器
    {
        //構造一個長方形
        this.width = width;
        this.height = height;
    }
}

我給代碼中的構造方法都加了代號。以上的demo,咱們能夠分析得出,若是使用B方法來構造對象,它的構造鏈是:A->C->B。若是用C方法來構造對象,構造鏈是:A->C。構造鏈是清晰的,就沒有問題。

對象

修飾符

重申一下何爲修飾符:應用於類型或者成員的關鍵字。blog

可見性修飾符

修飾符
不能把類型定義爲protected,private,protected interval。嵌套類除外,由於類成員可使用訪問限制性修飾符,而嵌套類是和類的成員同等級的。

其餘修飾符

修飾符


講完繼承,下篇開始講解接口。



歡迎關注本人微信公衆號,更及時的關注最新文章(每週多篇原創文章,以及多篇專題文章):

微信公衆號 掃描二維碼關注

相關文章
相關標籤/搜索