摘要
我們都知道,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
