• 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

  • 相关阅读:
    对象的存在是因为别人要用它
    图书馆借书程序我的分析
    版权迷思
    第四章 算法
    不可持续的天才教育
    第一章 你要做什么?
    win8 开发新格局分析
    水果机的几率设计
    我的理想
    windows 8,微软创新之路
  • 原文地址:https://www.cnblogs.com/ShimizuShiori/p/5684606.html
Copyright © 2020-2023  润新知