我知道“匈牙利标记”指的是将变量、参数或类型的信息作为前缀添加到其名称中。尽管在某些情况下似乎是一个好主意,但所有人似乎都非常反对它。如果我觉得有用的信息被传递了,为什么不把它放在那里让它更加易于使用呢?
另请参阅:人们是否在实际工作中使用匈牙利命名约定?
我知道“匈牙利标记”指的是将变量、参数或类型的信息作为前缀添加到其名称中。尽管在某些情况下似乎是一个好主意,但所有人似乎都非常反对它。如果我觉得有用的信息被传递了,为什么不把它放在那里让它更加易于使用呢?
另请参阅:人们是否在实际工作中使用匈牙利命名约定?
使用匈牙利命名法使得阅读代码变得困难。
大多数人错误地使用匈牙利命名法,并获得错误的结果。
请阅读 Joel Spolsky 的优秀文章:使错误的代码看起来像错误。
简而言之,将变量名以其类型
(字符串)(系统匈牙利命名法)作为前缀的匈牙利命名法是无用的,因此是不好的。
作者旨在使用匈牙利命名法,在变量名前面加上其种类
(使用 Joel 的例子:安全字符串或不安全字符串),所谓的应用程序匈牙利命名法具有其用途,仍然很有价值。
Joel错了,以下是原因。
他提到的那些“应用程序”信息应该被编码在类型系统中。你不应依靠翻转变量名来确保将不安全的数据传递给需要安全数据的函数。你应该将其设置为类型错误,以便根本不可能这样做。任何不安全的数据都应有一个标记为不安全的类型,以便它不能简单地传递给安全函数。从不安全到安全的转换应该需要使用某种类型的清理函数进行处理。
然而,大多数语言缺少足够表达这种区别的类型系统。例如,如果C有一种“强制typedef”(其中typedef名称具有基本类型的所有操作,但不能转换为它),那么很多这些问题就会消失。例如,如果您可以说,strong typedef std::string unsafe_string;
引入一个新类型unsafe_string
,它不能转换为std :: string(因此可以参与重载解析等等),那么我们就不需要愚蠢的前缀。
所以,关于匈牙利语的中心论点是错误的。它正在用于类型信息。当然,比传统的C类型信息更丰富;它是一种编码一定的语义细节以指示对象目的的类型信息。但它仍然是类型信息,正确的解决方案始终是将其编码到类型系统中。将其编码到类型系统中远远是获得规则的适当验证和强制执行的最佳方法。变量名称根本达不到标准。
换句话说,目标不应该是“使错误代码对开发人员看起来像错误”。它应该是“使错误代码在编译器中看起来像错误”。
我认为这会大大地使源代码变得混乱不堪。
而在强类型语言中,这并没有带来太多的好处。如果你做了任何类型不匹配的操作,编译器都会告诉你。
我在所有项目中都使用匈牙利命名法。当我处理数百个不同的标识符名称时,我发现这很有帮助。
例如,当我调用需要字符串的函数时,我可以输入“s”,然后按下控制空格键,我的IDE将显示所有以“s”为前缀的变量名称。
另一个优点是,当我将u用于无符号整数和i用于有符号整数时,我立即看到了可能危险的混合有符号和无符号的情况。
我记不得有多少次,在一个巨大的75000行代码库中,由于将本地变量命名为该类的现有成员变量的相同名称,导致出现错误(包括我和其他人)。从那时起,我总是在成员变量前加上“m_”。
这是品味和经验的问题。在你尝试之前不要轻易否定它。
你忘了包含此信息的头号原因。与你这个程序员无关,而是与2到3年后要阅读那些代码的人有关。
是的,在IDE中可以快速为您标识类型。然而,当您在阅读一些长长的“业务规则”代码时,不必每次都停下来查找它是什么类型,这很好。当我看到像strUserID、intProduct或guiProductID这样的东西时,让“入门”的时间变得轻松多了。
我同意微软在一些命名约定方面过分了-我把它归类为“过犹不及”的一堆中。
命名约定是好事情,只要你坚持它们。我经历了足够多的旧代码,让我不断返回查看许多同名变量的定义,所以我推崇“驼峰式命名”(在以前的工作中被称为)。现在我正在一份有数千行完全没有注释的Classic ASP代码和VBScript上工作,这真是一个噩梦,难以弄清楚事情发生了什么。
在每个变量名的开头添加晦涩的字符是不必要的,这表明变量名本身并不具有描述性。大多数语言在声明时需要变量类型,因此该信息已经可用。
而且,在维护期间可能会出现变量类型需要更改的情况。例如:如果一个被声明为“uint_16 u16foo”的变量需要成为64位无符号整型,那么有两种可能的情况:
Joel Spolsky写了一篇关于这个问题的好博客文章。 http://www.joelonsoftware.com/articles/Wrong.html 基本上,问题在于不要让你的代码变得更难读,当一个不错的IDE会告诉你变量的类型如果你记不住。另外,如果你把你的代码分成足够的部分,你就不必记得一个变量是在三页前声明的。
* l for local
* a for argument
* m for member
* g for global
* etc
通过现代重构旧代码的技术,如果你改变了某个符号的类型,使用搜索和替换是很繁琐的。虽然编译器会捕获类型更改,但通常无法捕获范围使用不正确的情况,因此合理的命名约定在这里非常有帮助。