不断滥用?
我在一些具有以下常量的C#项目中遇到了一堆代码:
const int ZERO_RECORDS = 0; const int FIRST_ROW = 0; const int DEFAULT_INDEX = 0; const int STRINGS_ARE_EQUAL = 0;
有没有人见过这样的东西? 有没有办法合理化使用常量来表示语言结构? IE:C#在数组中的第一个索引位于第0位。我认为如果开发人员需要依赖常量来告诉他们语言是0,那么手头有一个更大的问题。
这些常量的最常见用法是处理数据表或“for”循环。
我是不是觉得这些是代码味道? 我觉得这些并不比以下更好:
const int ZERO = 0; const string A = "A";
滥用,恕我直言。 “零”只是其中的基础之一。
虽然STRINGS_ARE_EQUAL可能很简单,但为什么不“.Equals”?
接受限制使用魔法数字?
我是不是觉得这些是代码味道? 我觉得这些并不比以下更好:
比较以下内容:
if(str1.CompareTo(str2) == STRINGS_ARE_EQUAL) ...
同
if(str1.CompareTo(str2) == ZERO) ... if(str1.CompareTo(str2) == 0) ...
哪一个更直接的意义?
这肯定是代码味道。
意图可能是为代码添加“可读性”,但是在我看来这样的事情实际上会降低代码的可读性。
有些人认为程序中的任何原始数字都是“神奇数字”。 我已经看到编码标准,基本上说你不能只是在程序中写一个整数,它必须是一个const int。
我是不是觉得这些是代码味道? 我觉得这些并不比以下更好:
const int ZERO = 0;
const int A =’A’;
可能有点嗅觉,但肯定比ZERO = 0和A =’A’好。 在第一种情况下,它们定义了逻辑常量,即具有具体值实现的一些抽象概念(字符串相等)。
在您的示例中,您定义了文字常量 – 变量表示值本身。 如果是这种情况,我认为枚举是首选,因为它们很少是奇异值。
这是明确的错误编码。
我说常量应该只在需要的地方使用,以后某些事情可能发生变化。 例如,我有很多“配置”选项,如SESSION_TIMEOUT
定义它应该保持不变,但也许它可以在以后调整。 我不认为ZERO
可以在路上调整。
此外,对于幻数,不应包括零。
我觉得这个信念有点奇怪,因为我会说这样的事情会走得太远
//input is FIELD_xxx where xxx is a number input.SubString(LENGTH_OF_FIELD_NAME); //cut out the FIELD_ to give us the number
你应该看看thedailywtf上的一些东西
One2Pt20462262185th
和
企业SQL
我认为有时人们盲目地遵循“编码标准” ,即“不要使用硬编码值,将它们定义为常量,以便在需要更新时更容易管理代码” – 这对于以下内容来说是公平的:
const in MAX_NUMBER_OF_ELEMENTS_I_WILL_ALLOW = 100
但是没有意义:
if(str1.CompareTo(str2) == STRINGS_ARE_EQUAL)
因为每当我看到这段代码时,我都需要搜索STRINGS_ARE_EQUAL
定义的内容 ,然后检查文档是否正确。
相反,如果我看到:
if(str1.CompareTo(str2) == 0)
我跳过步骤1(搜索STRINGS_ARE...
被定义为)并且可以检查规格0
表示什么值。
你会正确地想用Equals()
替换它,并在你只对一种情况感兴趣的情况下使用CompareTo()
,例如:
switch (bla.CompareTo(bla1)) { case IS_EQUAL: case IS_SMALLER: case IS_BIGGER: default: }
如果合适,使用if/else
语句(不知道CompareTo()
返回…)
我仍然会检查你是否根据规格正确定义了值。
这当然是不同的,如果规范定义像ComparisonClass::StringsAreEqual
值或类似的东西(我只是使那一个),那么你不会使用0但适当的变量。
所以它取决于,当你特别需要访问数组中的第一个元素时, arr[0]
比arr[FIRST_ELEMENT]
更好,因为我仍然会检查你所定义的FIRST_ELEMENT
因为我不会相信你,它可能是不同的东西比0
– 例如你的0
元素是dud,真正的第一个元素存储在1
– 谁知道。
我会寻找代码味道。 如果需要这些常量,请将它们放入枚举中:
enum StringEquality { Equal, NotEqual }
(但我怀疑STRINGS_ARE_EQUAL
是string.Compare
返回的string.Compare
,所以黑客返回枚举可能会更加冗长。)
编辑: SHOUTING_CASE
也不是特别的.NET风格的命名约定 。
我不知道我是否会称它们为气味,但它们看起来似乎是多余的。 虽然DEFAULT_INDEX实际上可能有用。
重点是避免神奇的数字和零并不是真的神奇。
这个代码是你办公室里的东西还是你下载的东西?
如果它在办公室,我认为如果人们随机放置常数,那么管理就会出现问题。 在全球范围内,除非每个人都清楚地了解或同意这些常数的用途,否则不应该有任何常数。
理想情况下,在C#中,您需要创建一个包含每个其他类全局使用的常量的类。 例如,
class MathConstants { public const int ZERO=0; }
然后在后面的课程中,例如:
.... if(something==MathConstants.ZERO) ...
至少我就是这样看的。 通过这种方式,每个人都可以理解这些常量甚至没有阅读任何其他内容。 这会减少混乱。
使用常量我通常会想到四个原因:
- 作为可能在未来合理变化的值的替代(例如,
IdColumnNumber = 1
)。 - 作为可能不易理解或有意义的价值的标签(例如
FirstAsciiLetter = 65
), - 作为键入冗长或难以键入值的较短且不易出错的方式(例如,
LongSongTitle = "Supercalifragilisticexpialidocious"
) - 作为难以记忆的值的记忆辅助(例如,
PI = 3.14159265
)
对于您的特定示例,以下是我如何判断每个示例:
const int ZERO_RECORDS = 0; // almost definitely a code smell const int FIRST_ROW = 0; // first row could be 1 or 0, so this potentially fits reason #2, // however, doesn't make much sense for standard .NET collections // because they are always zero-based const int DEFAULT_INDEX = 0; // this fits reason #2, possibly #1 const int STRINGS_ARE_EQUAL = 0; // this very nicely fits reason #2, possibly #4 // (at least for anyone not intimately familiar with string.CompareTo())
所以,我会说,不,这些并不Zero = 0
或A = "A"
。
如果零表示零以外的东西(在这种情况下为STRINGS_ARE_EQUAL)那么那就是神奇的。 为它创建一个常量是可以接受的,并使代码更具可读性。
创建一个名为ZERO的常量是毫无意义的,浪费手指能量!
闻到一点,但我可以看到这种情况有意义的情况,特别是如果你有程序员一直从语言切换到语言。
例如,MATLAB是单索引的,所以我可以想象有人厌倦了在切换语言时犯下一个错误,并在C ++和MATLAB程序中定义DEFAULT_INDEX来抽象差异。 不一定优雅,但如果这是它需要的……
对,你要问这个气味年轻的代码战士。 但是,这些命名常量来自编码实践,远远超过Visual Studio的曙光。 它们可能是多余的,但你可能比理解公约的起源更糟糕。 想想NASA电脑,回来的时候……
您可能会在跨平台的情况下看到类似这样的内容,您可以将该文件与适合该平台的常量集一起使用。 但可能不是这些实际例子。 这看起来像COBOL编码器试图使他的C#看起来更像英语(没有针对COBOL编码器的攻击)。
使用常量来表示抽象值是可以的,但用你自己的语言表示构造则是另一种。
const int FIRST_ROW = 0
没有意义。
const int MINIMUM_WIDGET_COUNT = 0
更有意义。
你应该遵循编码标准的假设是有道理的。 (也就是说,编码标准在组织内是推定正确的。)在不满足推定时严格遵循编码标准是没有意义的。
因此,我同意早期的海报,一些臭味的常数可能是因为遵循编码标准(“无魔数”)而无例外。 这就是问题所在。