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

原文地址:https://www.cnblogs.com/ShimizuShiori/p/5684606.html