代码之家  ›  专栏  ›  技术社区  ›  Rigobert Song

如何重构此代码?

c#
  •  2
  • Rigobert Song  · 技术社区  · 15 年前

    double p = (Convert.ToDouble(inta) / Convert.ToDouble(intb)) * 100;
    double v = (p / 100) * Convert.ToDouble(intc);
    return (int)v;
    

    这对我来说似乎很混乱,我知道我可以把它压在一条线上,但我很想知道其他人会怎么做。

    谢谢

    9 回复  |  直到 15 年前
        1
  •  7
  •   LukeH    15 年前

    假设 inta intb intc 键入为 int Int32 然后 Convert.ToDouble 基本上和一个简单的 double

    return (int)((inta / (double)intb) * intc);
    

    这是否真的值得重构是另一回事。将中间计算保持为单独的语句以提高可读性通常更有意义,即使您不需要这些中间结果。当然,拥有有意义的变量名会使 大的

        2
  •  4
  •   FrustratedWithFormsDesigner    15 年前

    说真的,不要。代码有什么问题-忽略可能的数学问题 只是 看看代码结构本身?

    绝对有

    //set up variables
    double doubleA = Convert.ToDouble(inta);
    double doubleB = Convert.ToDouble(intb);
    double doubleC = Convert.ToDouble(intc);
    
    
    //do calculations
    double p = (doubleA / doubleB) * 100
    double v = (p / 100) * doubleC; //why did we divide by 100 when we multiplied by it on the line above?
    return (int)v; //why are we casting back to int after all the fuss and bother with doubles?
    

    但我真的宁愿别管它!

        3
  •  4
  •   Pete Kirkham    15 年前

    好吧,首先我会使用更有意义的名称,猜测这是取整数的比率,将其转换为百分比,将该百分比应用于另一个原始值,并返回一个新值,这是截断为整数的结果。

    double percent = (Convert.ToDouble( numer ) / Convert.ToDouble( denom )) * 100;
    double value = (percent / 100) * Convert.ToDouble( originalValue );
    return (int)value;
    

    使用Convert和cast的一个区别是Convert将抛出一个越界异常,但casting不会,而casting to in t将导致Int32.MinValue。所以如果 value 对于int来说太大或太小,或者 Infinity NaN

    因此,您可以使用强制转换来编写它,而不改变其含义,并利用这样一个事实:在包含int和double的表达式中,int被自动强制转换为double:

    double percent = ((double) numer ) /  denom ) * 100;
    double value = (percent / 100) * originalValue;
    return (int)value;
    

    现在,C#将赋值的双结果截断为15-16,但它的实现定义了中间产物是否以更高的精度操作。我不认为这会改变可转换为int的范围内的输出,但我不知道,值空间太大,无法进行详尽的测试。所以没有 确切地 函数的目的是做什么,您可以改变的很少,并确保您不会改变输出。

        static int test0(int numer, int denom, int initialValue)
        {
            double percent = (Convert.ToDouble(numer) / Convert.ToDouble(denom)) * 100;
            double value = (percent / 100) * Convert.ToDouble(initialValue);
            return (int)value;
        }
    
        static int test1(int numer, int denom, int initialValue)
        {
            return (int)((((((double)numer) / denom) * 100 ) / 100 ) * initialValue);
        }
    
        static int test2(int numer, int denom, int initialValue)
        {
            return (int)((((double)numer) / denom) * initialValue);
        }
    
        static int test3(int numer, int denom, int initialValue)
        {
            return (int)((((double)numer) * initialValue) / denom);
        }
    
        static int test4(int numer, int denom, int initialValue)
        {
            if (denom == 0) return int.MinValue;
            return (numer * initialValue / denom);
        }
    

    然后你会得到以下计算次数的结果 testN 不等于 test0

    numer in [-10000,10000] 
    denom in [-10000,0) (0,10000] 
    initialValue in [-10000,-8709] # will get to +10000 eventually
    
    test1 fails = 0 of 515428330128 tests, 100% accuracy.
    test2 fails = 110365664 of 515428330128 tests, 99.9785875828803% accuracy.
    test3 fails = 150082166 of 515428330128 tests, 99.9708820495057% accuracy.
    test4 fails = 150082166 of 515428330128 tests, 99.9708820495057% accuracy.
    

    如果你想要一个完全等价的函数 test1 test2 ,实际上它们确实会在一些边缘情况下影响结果-中间值的舍入会将值推到整数的一侧或另一侧。对于这个测试,输入值在-10000到+10000之间,因此 test4 不会溢出,所以 test3 试验4 都是一样的。对于更宽的输入范围,test4将更频繁地偏离。

    始终根据自动化测试验证重构。也不要假设计算机所处理的值的行为与高中数学中的数字一样。

        4
  •  1
  •   Liviu Mandras    15 年前

    前两行可以组合:

    double pv = ((double)inta/intb)*intc;
    return (int)pv;
    
        5
  •  0
  •   George Johnston    15 年前
    return (int)(Convert.ToDouble(inta * intc) / Convert.ToDouble(intb));
    
        6
  •  0
  •   Evan Mulawski    15 年前
    return (int)(((double)inta / intb) * intc);
    

        7
  •  0
  •   egrunin    15 年前

    在@FrustedWithformsdes的答案上有一个变体:

    double doubleA = (double) (inta * intc);
    double doubleB = (double) intb;
    
    return (int) (doubleA / doubleB);
    
        8
  •  0
  •   Jason Williams    15 年前

    有几个有趣的观点,似乎没有其他人涉及,所以我将添加到混合。。。

    • 如果“inta”等不能转换为double,Convert.ToXXX可能引发异常。在这种情况下,您可以使用double.TryParse()或try…catch使此代码对任何类型都具有健壮性。(当然,正如许多人提到的,如果值只是int,那么(double)cast就足够了)。
    • 如果intb的值为0,则会出现被零除的异常。因此,在使用intb之前,您可能需要检查它是否为非零。
    • 对数学来说。。。*100和/100将取消,因此没有意义。假设输入是整数(而不是大数),那么如果在除法之前用intc预乘inta,就可以消除一个(双)运算,因为(int*intc)可以以整数精度安全地完成。

    return((int) ((inta * intc) / (double) intb));
    

    它与公认的答案没有太大的不同,但在某些平台上可以表现得稍好一些(使用整数乘法而不是双乘法)。

        9
  •  0
  •   Sanjay Manohar    15 年前

    当然这是公正的

    inta * intc / intb