为什么我不应该使用 "匈牙利命名法"?

122

我知道“匈牙利标记”指的是将变量、参数或类型的信息作为前缀添加到其名称中。尽管在某些情况下似乎是一个好主意,但所有人似乎都非常反对它。如果我觉得有用的信息被传递了,为什么不把它放在那里让它更加易于使用呢?

另请参阅:人们是否在实际工作中使用匈牙利命名约定?


相关:https://dev59.com/OnVC5IYBdhLWcg3wtzut - Lance Roberts
37个回答

276

使用匈牙利命名法使得阅读代码变得困难。


101
很好的比喻,虽然有点不公平。对比一下: 使用匈牙利标记法会使代码阅读变得困难。 - Chris Noe
27
那句话中,Using和Reading都是动名词。但我理解你的意思了… - chimp
28
@chimp:这使得事情更加明显了。既然你已经发现类型错误,你打算重新命名所有引用还是保持原样,提供错误的信息?无论哪种方式都会让你失去。当然,这是错误的匈牙利命名法应用方式。 - wilhelmtell
1
这个类比不成立,因为没有添加任何信息,你已经知道一个单词是否是名词。但是如果我有一个叫做“count”的变量,它是整数还是字符串(例如显示给用户)呢? - Jon
3
@Jon:如果你有一个变量名并不能完全表达它代表的含义,那么务必要将其重命名为更具描述性的名称(匈牙利标记法并不是一种改进,在极少数情况下,它只是解决了症状而非问题本身)。 - hlovdal
显示剩余6条评论

174

大多数人错误地使用匈牙利命名法,并获得错误的结果。

请阅读 Joel Spolsky 的优秀文章:使错误的代码看起来像错误

简而言之,将变量名以其类型(字符串)(系统匈牙利命名法)作为前缀的匈牙利命名法是无用的,因此是不好的。

作者旨在使用匈牙利命名法,在变量名前面加上其种类(使用 Joel 的例子:安全字符串或不安全字符串),所谓的应用程序匈牙利命名法具有其用途,仍然很有价值。


5
好的链接,良好的概述链接背后的内容,不带任何盲目热情。非常好。 - Dustman
12
请注意,Joel的示例是使用VBScript编写的,这是一种长时间没有用户定义类的语言。在面向对象的语言中,您只需创建一个HtmlEncodedString类型,并使Write方法仅接受该类型即可。"应用程序匈牙利命名法"仅在没有用户定义类型的语言中有用。 - JacquesB
4
微软因过去滥用匈牙利命名法而闻名。在标识符前添加类型信息不仅是无用的,而且可能会造成伤害。首先,可读性较差。其次,在支持多态或鸭子类型的语言中,会传递错误的信息。 - wilhelmtell
9
如果我在使用旧版IDE进行纯C开发,我仍然喜欢使用匈牙利命名法……这样我就知道没有类型转换问题。 - Beep beep
3
通常情况下,通过在一开始使用描述性的变量名称来避免这种情况。但如果你想确保的话,更安全的方法是为长度、重量等创建不同的类型(例如结构体),并使用运算符重载来确保只有有效的操作才能进行。 - JacquesB
显示剩余8条评论

104

Joel错了,以下是原因。

他提到的那些“应用程序”信息应该被编码在类型系统中。你不应依靠翻转变量名来确保将不安全的数据传递给需要安全数据的函数。你应该将其设置为类型错误,以便根本不可能这样做。任何不安全的数据都应有一个标记为不安全的类型,以便它不能简单地传递给安全函数。从不安全到安全的转换应该需要使用某种类型的清理函数进行处理。

然而,大多数语言缺少足够表达这种区别的类型系统。例如,如果C有一种“强制typedef”(其中typedef名称具有基本类型的所有操作,但不能转换为它),那么很多这些问题就会消失。例如,如果您可以说,strong typedef std::string unsafe_string;引入一个新类型unsafe_string,它不能转换为std :: string(因此可以参与重载解析等等),那么我们就不需要愚蠢的前缀。

所以,关于匈牙利语的中心论点是错误的。它正在用于类型信息。当然,比传统的C类型信息更丰富;它是一种编码一定的语义细节以指示对象目的的类型信息。但它仍然是类型信息,正确的解决方案始终是将其编码到类型系统中。将其编码到类型系统中远远是获得规则的适当验证和强制执行的最佳方法。变量名称根本达不到标准。

换句话说,目标不应该是“使错误代码对开发人员看起来像错误”。它应该是“使错误代码在编译器中看起来像错误”。


30
变量的意图应该被编码在变量的类型中,而不是留给像该死的命名方案这样脆弱的东西。 - DrPizza
3
我很愿意让编译器/解释器来完成这个任务,但对于动态语言(如Python、Ruby、PHP或Javascript)来说,这将带来巨大的开销。 - too much php
22
然而,大多数语言缺乏一个足够表达这些区别的类型系统来强制执行它们。这就是问题所在。AppsHungarian是一种实用的方法,可以通过在代码中明显地使用这些语言来突出问题,而不是检查错误报告。 - Neil Trodden
6
@DrPizza: 我认为你在提倡Apps Hungarian时错过了一个非常重要的观点:这种记法只是一种非常实用的权衡。使编译器能够看到“错误代码”是一个伟大的目标,但它是否总是值得代价呢? - Yarik
4
@DrPizza说:“Joel错了”,并且“然而,大多数语言缺乏一种足够表达这些区别的类型系统。” 因此,由于大多数语言确实缺乏足够表达这些区别的能力,所以Joel是正确的。 是这样吗? - sampathsris
显示剩余15条评论

46

我认为这会大大地使源代码变得混乱不堪。

而在强类型语言中,这并没有带来太多的好处。如果你做了任何类型不匹配的操作,编译器都会告诉你。


哇,15分钟内获得90个声望值。做得好。 - Dustman
2
如果你用它来表示类型,那么是没用的。但它可以传递其他信息,还是有用的。 - Lou Franco
12
@Lou,没错 - 就个人而言,我在变量名中存储了修订历史记录:字符串 firstName_wasName_wasNameID_wasSweetCupinCakes。 - Shog9
@nsanders:尝试使用编译器检查当两个变量类型为整数时是否使用了相同的度量单位。 - Igor Levicki
1
@Shog9:我认为您没有理解应用程序匈牙利命名法。它更多的是关于sFirstName与unFirstName(安全与不安全,未经净化),这样您就可以更多地了解它。尽管如此,在静态语言中,我认为更好的方法是对字符串进行子类型化,使其成为UnsafeString和SafeString,并且只允许SafeString输出。约定可以,但(更常见的)编译强制执行更好。 - kgadek
@Shog9 那样更糟糕。那样会导致疯狂。这就是为什么我们有源代码控制的原因。 - CJBrew

28
匈牙利命名法只在没有用户定义类型的语言中才有意义。在现代函数式或面向对象语言中,您应该将有关“值类型”的信息编码到数据类型或类中,而不是变量名称中。
一些答案参考了Joels文章。请注意,他的示例是在VBScript中完成的,VBScript长时间内不支持用户定义类。在具有用户定义类型的语言中,您可以通过创建一个HtmlEncodedString类型,然后让Write方法仅接受该类型来解决相同的问题。在静态类型语言中,编译器将捕获任何编码错误,在动态类型语言中,您将获得运行时异常 - 但无论如何,都会受到保护以防止编写未编码的字符串。匈牙利命名法只是将程序员转换为人工类型检查器,而这种工作通常最好由软件处理。
Joel区分了“系统匈牙利”和“应用程序匈牙利”,其中“系统匈牙利”对内置类型(如int,float等)进行编码,“应用程序匈牙利”对“种类”进行编码,这是关于变量的高级元信息超出机器类型。在面向对象或现代函数式语言中,您可以创建用户定义类型,因此在这个意义上类型和“种类”之间没有区别 - 两者都可以由类型系统表示 - “应用程序”匈牙利和“系统”匈牙利一样多余。
所以回答你的问题:在不安全的、弱类型语言中,例如将浮点值赋给整数变量会导致系统崩溃的情况下,“系统匈牙利命名法”才有用。匈牙利命名法是特别为BCPL这种完全没有进行任何类型检查的低级语言而发明的。我认为今天一般使用的语言都没有这个问题,但这种命名法作为一种空洞编程的形式一直存在。
如果你正在使用没有用户定义类型的语言,如旧版VBScript或早期版本的VB,或许还包括早期版本的Perl和PHP,那么“应用程序匈牙利命名法”就有意义了。同样,在现代语言中使用它纯粹是一种空洞编程。
在任何其他语言中,匈牙利命名法只是丑陋、冗余和脆弱的。它重复了从类型系统已知的信息,不应该重复自己。对于描述类型的实例的意图,使用一个描述性的变量名。使用类型系统来编码关于变量“种类”或“类”的不变量和元信息-即类型。
Joel文章的一般观点-使错误代码看起来像错误-是一个非常好的原则。然而,更好的防止错误的保护是-尽可能地-使错误代码被编译器自动检测到。

具有用户定义类型的编程语言并不总是实用,使用类型代替"种类"。考虑Joel的Apps Hungarian中的前缀"c"、"d"、"ix"。在所有现代语言中,您是否为它们使用自定义整数类型?我认为不会。因为现代语言仍然没有内置的自定义整数类型用于索引、计数和两个数字之间的差异。只要我们仍在使用普通的int类型,Apps Hungarian将是非冗余且有用的。 - Eldritch Conundrum

27

我在所有项目中都使用匈牙利命名法。当我处理数百个不同的标识符名称时,我发现这很有帮助。

例如,当我调用需要字符串的函数时,我可以输入“s”,然后按下控制空格键,我的IDE将显示所有以“s”为前缀的变量名称。

另一个优点是,当我将u用于无符号整数和i用于有符号整数时,我立即看到了可能危险的混合有符号和无符号的情况。

我记不得有多少次,在一个巨大的75000行代码库中,由于将本地变量命名为该类的现有成员变量的相同名称,导致出现错误(包括我和其他人)。从那时起,我总是在成员变量前加上“m_”。

这是品味和经验的问题。在你尝试之前不要轻易否定它。


1
将有符号性编码到变量名中,当代码实际处理有符号性时是很好的。以此为借口强制约定所有变量的做法只是我个人认为的教条主义。 - Plumenator
这只是一个提高生产力的技巧,就像IDE自动完成一样。它本质上并没有什么邪恶之处。我相信有经验的程序员必须被允许使用他们自己的风格。强迫任何人选择某种风格都是不好的。我现在作为单个开发人员工作,但除非他们首先缺乏一致的风格,否则我不会强制要求与我合作的任何人遵循某种风格。 - rep_movsd
1
我当时点赞了这个,因为我支持匈牙利命名法。现在我后悔了,再也不会把前缀添加到变量上了。 - nawfal
75000行的代码库,竟然没有人打开警告标志来检查是否存在变量重名? - Trevor Hickey
不...那是VC 98,我不确定那个警告是否存在。 - rep_movsd
显示剩余4条评论

22

你忘了包含此信息的头号原因。与你这个程序员无关,而是与2到3年后要阅读那些代码的人有关。

是的,在IDE中可以快速为您标识类型。然而,当您在阅读一些长长的“业务规则”代码时,不必每次都停下来查找它是什么类型,这很好。当我看到像strUserID、intProduct或guiProductID这样的东西时,让“入门”的时间变得轻松多了。

我同意微软在一些命名约定方面过分了-我把它归类为“过犹不及”的一堆中。

命名约定是好事情,只要你坚持它们。我经历了足够多的旧代码,让我不断返回查看许多同名变量的定义,所以我推崇“驼峰式命名”(在以前的工作中被称为)。现在我正在一份有数千行完全没有注释的Classic ASP代码和VBScript上工作,这真是一个噩梦,难以弄清楚事情发生了什么。


作为一个VIM(没有IDE,没有高亮,没有悬停告诉你类型)用户,我完全不理解这个论点。我自然而然地理解,一个名为name的变量是一个字符串,一个名为i或count的变量是一个int。真的需要sName和iCount吗?它到底有什么帮助呢?我认为你的产品ID示例是一个特殊情况,也许在那里使用它会很好。但即使如此,在所有地方都使用int或string来表示产品ID并保持一致将是一个更好的解决方案,不是吗?整个命名问题似乎是一个借口。 - MrFox
6
这是为了保持一致性。是的,“name”本质上意味着一个字符串。但你会认为“userid”是什么?GUID?字符串?整数?即使像“acctno”这样的名称也可能具有值42或“23X”。在大多数代码中,文档很少。这个标准可以帮助后续的程序员进行维护。 - David
1
@David 我完全同意你的答案——一眼就知道变量的意图是我喜欢前缀的原因,特别是在像 JS 这样的动态语言中。 - Daniel Sokolowski

10

在每个变量名的开头添加晦涩的字符是不必要的,这表明变量名本身并不具有描述性。大多数语言在声明时需要变量类型,因此该信息已经可用。

而且,在维护期间可能会出现变量类型需要更改的情况。例如:如果一个被声明为“uint_16 u16foo”的变量需要成为64位无符号整型,那么有两种可能的情况:

  1. 您需要逐个更改每个变量名(确保不会影响任何具有相同名称的不相关变量),或者
  2. 只更改类型而不更改名称,这将导致混淆。

9

Joel Spolsky写了一篇关于这个问题的好博客文章。 http://www.joelonsoftware.com/articles/Wrong.html 基本上,问题在于不要让你的代码变得更难读,当一个不错的IDE会告诉你变量的类型如果你记不住。另外,如果你把你的代码分成足够的部分,你就不必记得一个变量是在三页前声明的。


9
如今范围不比类型更重要吗,例如:
* l for local
* a for argument
* m for member
* g for global
* etc

通过现代重构旧代码的技术,如果你改变了某个符号的类型,使用搜索和替换是很繁琐的。虽然编译器会捕获类型更改,但通常无法捕获范围使用不正确的情况,因此合理的命名约定在这里非常有帮助。


再说,代码可能不应该那么依赖作用域。 :-) - Plumenator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接