在我的代码中硬编码复杂的数学逻辑是否可以?

时间:2013-09-10 17:57:06

标签: c#

是否有一种普遍接受的编码复杂数学的最佳方法?例如:

double someNumber = .123 + .456 * Math.Pow(Math.E, .789 * Math.Pow((homeIndex + .22), .012));

这是一个硬编码数字的点吗?或者每个数字是否都有与之相关的常数?或者还有其他方法,比如在配置中存储计算并以某种方式调用它们?

会有很多像这样的代码,我试图让它保持可维护性。

注意:上面显示的示例只是一行。这些代码行将有数十或数百行。而且不仅数字会改变,而且公式也可以改变。

8 个答案:

答案 0 :(得分:20)

通常,有两种常量 - 对实现有意义的常量,以及对业务逻辑有意义的常量。

可以对第一类常量进行硬编码:它们对于理解算法是私有的。例如,如果您使用ternary search并且需要将间隔分为三个部分,则除以硬编码的3是正确的方法。

另一方面,具有程序代码之外含义的常量不应该是硬编码的:赋予它们明确的名称可以让您在离开公司之后维护代码的人有非零的机会进行正确的修改不得不从头开始重写或通过电子邮件向您寻求帮助。

答案 1 :(得分:4)

“没关系”?当然。据我所知,没有一支准军事警察部队围捕那些违背编程的真正信仰的人。 (然而。)。

这是明智的吗?

嗯,有各种各样的决定方法 - 性能,可伸缩性,可扩展性,可维护性等。

在可维护性范围内,这是纯粹的邪恶。它使得可扩展性非常难;性能和可扩展性可能不是一个大问题。

如果你留下一个方法,其中包含类似于上面的大量行,你的继任者将无法维护代码。他建议改写是正确的。

如果你将其分解为

public float calculateTax(person)
  float taxFreeAmount = calcTaxFreeAmount(person)
  float taxableAmount = calcTaxableAmount(person, taxFreeAmount)
  float taxAmount = calcTaxAmount(person, taxableAmount)
  return taxAmount
end

并且每个内部方法都是几行,但是你在那里留下了一些硬编码的值 - 好吧,不是很棒,但并不可怕。

但是,如果某些硬编码值可能会随着时间的推移而变化(如税率),则将它们保留为硬编码值并不合适。太可怕了。

我能给出的最好建议是:

  • 与Resharper共度一个下午,并使用其自动重构工具。
  • 假设那个从你身上捡起来的人是一个挥舞着斧头的疯子,他知道你住在哪里。

答案 2 :(得分:2)

我经常问自己,在编写代码六个月后,我是否可以在凌晨3点维护并修复代码。它对我有好处。看看你的公式,我不确定我能不能。

年龄前我在保险业工作过。我的一些同事的任务是将精算公式转换为代码,首先是FORTRAN和后来的C.数学和编程技能因同事而异。我学到的是以下回顾他们的代码:

  • 记录代码中的实际公式;没有它,多年以后你会记得实际的公式。外部文档丢失,变得过时或者可能无法访问。
  • 将公式分解为可记录,重复使用和测试的离散组件。
  • 使用常数来记录方程式; magic 数字的上下文非常少,并且通常需要现有的知识才能让其他开发人员理解。
  • 依靠编译器尽可能优化代码。一个好的编译器将内联方法,减少重复并优化特定体系结构的代码。在某些情况下,它可能会复制部分配方以获得更好的性能。

也就是说,有时硬编码只是简化了事情,特别是如果在特定的上下文中很好地理解了这些值。例如,将某个值除以(或乘以)100或1000,因为您将值转换为美元。另一个是当你想要将小时数转换为秒时,将某些东西乘以3600。它们的意义往往来自更大的背景。以下内容并未详细说明幻数100:

public static double a(double b, double c)
{
     return (b - c) * 100;
} 

但以下内容可能会给你一个更好的提示:

public static double calculateAmountInCents(double amountDue, double amountPaid)
{
     return (amountDue - amountPaid) * 100;
}

答案 3 :(得分:1)

正如上述评论所述,这远非复杂。

然而,您可以将 Magic 数字存储在常量/ app.config值中,以便下一位开发人员更容易保护您的代码。

存储此类常量时,请务必向下一位开发人员解释(1个月内自己阅读)您的想法是什么,以及需要记住的内容。

另外,ewxplain实际计算的内容和内容。

答案 4 :(得分:1)

不要像这样离线。

常量,以便您可以重复使用,轻松查找,轻松更改,并在有人第一次查看您的代码时提供更好的维护。

如果可以/应该自定义,您可以进行配置。客户改变价值的影响是什么?有时最好不要给他们这个选择。当事情不起作用时,他们可以自己改变它然后责备你。然后,也许他们比你的发布时间表更频繁地使用它。

答案 5 :(得分:0)

值得注意的是C#编译器(或它是CLR)将自动内联1行方法,因此如果您可以将某些公式提取到一个衬里中,您可以将它们作为方法提取而不会造成任何性能损失。

编辑:

常数等等或多或少取决于团队和使用量。显然,如果你不止一次使用相同的硬编码数字,那就不变了。但是,如果您正在编写一个公式,它可能只会编辑(小团队),那么硬编码值就可以了。这完全取决于您的团队对文档和维护的看法。

答案 6 :(得分:0)

如果您的行中的计算为下一个开发人员解释了一些内容,那么您可以保留它,否则最好在代码或配置文件中计算常量值。

我在生产代码中发现了一行,如:

int interval = 1 * 60 * 60 * 1000; 

没有任何评论,原始开发人员在几毫秒内意味着1 hour,而不是看到3600000的值。

IMO可能会遗漏计算对于这样的场景更好。

答案 7 :(得分:0)

可以添加名称以用于文档目的。所需的文件数量在很大程度上取决于目的。

请考虑以下代码:

 float e = m * 8.98755179e16;

与下面的对比:

 const float c = 299792458;
 float e = m * c * c;

即使后者中的变量名称不是非常“描述性”,您也可以更好地了解代码在第一个中执行的操作 - 可以说没有必要将c重命名为{{ 1}},speedOfLightmmass能量,因为名称在其域中是解释性的。

e

我认为第二个代码是最清晰的 - 特别是如果程序员可以期望在代码中找到STR(LHC模拟器或类似的东西)。总结一下 - 你需要找到一个最佳点。代码越详细,您提供的上下文越多 - 这可能有助于理解其含义( const float speedOfLight = 299792458; float energy = mass * speedOfLight * speedOfLight; e与我们对光的质量和速度做什么相比)并模糊大局(我们将c平方并乘以m与扫描整条线的需要得到方程式)。

大多数常数都有一些更深层次的修饰和/或已建立的符号所以我会考虑至少按惯例命名(c表示光速,c表示气体常数,R以小时为单位)。如果表示法不明确,则应使用较长的名称(名为sPerH的班级中的sPerHDate可能正常,但不在Time中。真正明显的常量可以是硬编码的(例如,在计算合并排序中的新数组长度时除以2)。