C#構造函數在繼承時必須要求與父類型構造函數入參相同怎么辦?


摘要

我們都知道,C#中,在類型繼承時,由於構造子類必須先構造其父類型的內容,因此,必須子類型的構造函數中調用父類型的構造函數(無參數的不需要顯式聲明)。

但是往往我們會出現,子類型本身的構造函數大於或小於父類型構造函數的情況,那我們應該怎么辦呢?

 


 

簡單情景:父類型需要兩個參數,而子類型只需一個參數

比如我們有一個專門用來計算兩個數相乘的類型:

class Multi
{
    public int Result { get; private set; }

    public Multi(int i,int j)
    {
        this.Result = i * j;
    }
}

 

然后,乘法中有一個特殊的情況就是平方,如果我們再建立一個類型用於直接計算平方的話,那構造函數只需要一個值就行了。

但是由於我們繼承的父類型的構造函數有兩個參數,所有我們要使用一些特殊的語法來標明,子類如何調用父類型的構造函數:

class Squ : Multi
{
    public Squ(int i)
        : base(i, i) //通過這行代碼,表示在new Squ(i)時,執行new Multi(i,i);
    {

    }
}

 

 


 

復雜情景:子類型構造時的參數,不能直接用於父類型的構建,需要經過非常復雜的過程才能得到父類型的構建參數

這是一種極少數情況下會遇到情況。

但是遇到以后,如果經驗不足,大家也會不知道如何下手處理。

我們繼續使用上面的Multi作為父類型,實現一個子類,用於“計算一元二次方程中的一正整數解”的子類出來。

 

——呃,這怎么可能。。。。。

想必大家的第一反應是這樣的。

 

那我們就來仔細分析一下,一元二次方程的求根公式是 ( -b ± √(b * b - 4 * a * c) / (2 * a)

除一個數,其實就是乘以它的相反數嘛。

於是這就變成了構建一個Multi對象,第一個參數是-b ± √(b * b - 4 * a * c),第二個參數是 1 / (2 * a)嘛

但是我們的命題是“正整數解”也就是說,我們還要加入一些判斷邏輯在里面,在僅僅的一行base(xxx,yyy)中間,我們有辦法實現這么多代碼嗎?

 

答案很簡單:當然沒辦法在base中寫入這么多代碼!最糟糕的是,我們還只能在base里面寫這些復雜的邏輯。

 

————那。。。。該如何時好呢?

 

答案就是“靜態方法”,靜態函數在類型第一次被訪問時就已經初始化好了,那么在實例化時,更不用,早就存在內存中了。

通過靜態方法,以及ref或out關鍵字,我們可以以靜態函數作為媒介,創建出一個完全符合要求的base語句來。

class MyFunc : Multi
{
    private static int CtorExt(int a, int b, int c, ref int j)
    {
        var d = b * b - 4 * a * c; //求delta,與0的比較不在此示例中演示
        var sd = Math.Sqrt(d);     //求平方根
        var i1 = -b + sd;          //計算兩個根的分子
        var i2 = -b - sd;
        j = 2 * a;
        //判斷與j的符號性,當符號相同時(正數)返回
        //注明:返回整數形式僅示例作用
        if (i1 > 0 && j > 0) return (int)i1;
        if (i1 < 0 && j < 0) return (int)i1;
        if (i2 > 0 && j > 0) return (int)i2;
        if (i2 < 0 && j < 0) return (int)i2;
        throw new ApplicationException("無正數解");
    }

    public MyFunc(int a, int b, int c, int j)
        : base(CtorExt(a, b, c, ref j), j)
    {

    }
}

 

通過上面這種復雜的方式,我們在子類的構造函數中,執行了CtroExt這個靜態方法,這個方法返回了用於構建父類型的第一個參數i,還通過ref關鍵字,得到了用於構建父類型的第二個參數j,於是base語句得到了完美的使用。

但是美中不足的是,子類的構建函數多了一個j作為入參,但是外部調用的時候,這個j毫無意義(因為值是最終會被CtorExt所替換,為了保證一個好的調用環境,我們應該將這個構造函數設為私有,再為新增一個符合要求的構造函數

class MyFunc : Multi
{
    private static int CtorExt(int a, int b, int c, ref int j)
    {
        var d = b * b - 4 * a * c; //求delta,與0的比較不在此示例中演示
        var sd = Math.Sqrt(d);     //求平方根
        var i1 = -b + sd;          //計算兩個根的分子
        var i2 = -b - sd;
        j = 2 * a;
        //判斷與j的符號性,當符號相同時(正數)返回
        //注明:返回整數形式僅示例作用
        if (i1 > 0 && j > 0) return (int)i1;
        if (i1 < 0 && j < 0) return (int)i1;
        if (i2 > 0 && j > 0) return (int)i2;
        if (i2 < 0 && j < 0) return (int)i2;
        throw new ApplicationException("無正數解");
    }

    private MyFunc(int a, int b, int c, int j)
        : base(CtorExt(a, b, c, ref j), j)
    {

    }
    public MyFunc(int a, int b, int c)
        : this(a, b, c, 0) //因為j最終會被替代,因此這里隨便寫什么值都行
    {

    }
}

 

 


 

總結:

1、這種解決方案也是“封裝”思想的體現,將一個復雜的方法封裝,並直接調用,可以得到我們想要的結構,而不關心實現過程。

2、靜態函數是可以在造構函數剛發生時使用的,因為它“早已准備好了”

3、ref關鍵字在這里非常重要

4、這是一種閱讀性不是非常好的編方式,不到萬不得已時,盡可能不要使用。

 

 

原文地址 http://www.zizhusoft.com/note/show.aspx?id=a8240ee2-eeeb-4cb3-bc7e-00aec29476f2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM