为什么我不应该使用“匈牙利符号”?

我知道匈牙利语是指什么 – 提供有关variables,参数或types的信息作为其名称的前缀。 每个人似乎都很反感,尽pipe在某些情况下似乎是个好主意。 如果我觉得有用的信息正在传递,为什么我不能把它放在那里?

另见: 人们是否在现实世界中使用匈牙利的命名规则?

大多数人用错误的方式使用匈牙利符号,并得到错误的结果。

阅读Joel Spolsky撰写的优秀文章: 使错误的代码看起来错了 。

简而言之,匈牙利符号(Hungarian Notation)中的variables名称前加string(系统匈牙利语)是不好的,因为它是无用的。

匈牙利符号,因为它的作者打算在variables名称的前缀(使用乔尔的例子:安全的string或不安全的string),所以称为应用程序匈牙利有其用途,仍然是有价值的。

使用补充说明:

乔尔是错的,这是为什么。

他所说的“应用”信息应该在types系统中进行编码 。 您不应该依赖翻转variables名称,以确保不会将不安全的数据传递给需要安全数据的函数。 你应该把它作为一个types错误,所以这是不可能的 。 任何不安全的数据都应该有一个标记为不安全的types,这样它就不能传递给一个安全的函数。 从不安全转换到安全应该需要处理某种清理function。

乔尔所说的“种类”的很多东西都不是种类, 事实上,它们是types的。

然而,大多数语言缺乏的是一种足以expression这种区别的types系统。 例如,如果C有一种“strong typedef”(其中typedef名称具有所有基types的操作,但是不能转换为它),那么很多这些问题就会消失。 例如,如果你可以说, strong typedef std::string unsafe_string; 引入一个无法转换为std :: string的新typesunsafe_string (因此可以参与重载parsing等),那么我们就不需要愚蠢的前缀。

所以,匈牙利人认为不是types的东西的核心主张是错误的。 它被用于types信息。 比传统的C型信息更丰富的types信息,当然; 它是编码某种语义细节的types信息,以指示对象的目的。 但是它仍然是types信息,并且正确的解决scheme一直是将其编码到types系统中。 将其编码到types系统中,是获取适当的规则validation和执行的最佳方法。 变数名称根本不会削减芥末。

换句话说,目的不应该是“把错误的代码看成错误的开发者”。 它应该是“使错误的代码看起来不对编译器 ”。

我认为它大量地混淆了源代码。

在强types语言中,它也不会带来太多好处。 如果你做任何forms的types错误tomfoolery,编译器会告诉你。

匈牙利语符号仅在没有用户定义types的语言中才有意义。 在现代函数或OO语言中,可以将有关“种类”值的信息编码到数据types或类中,而不是编码到variables名称中。

几个答案引用Joels的文章 。 请注意,他的例子是在VBScript中,它不支持用户定义的类(至less很长一段时间)。 在用户定义types的语言中,你可以通过创build一个HtmlEncodedStringtypes来解决同样的问题,然后让Write方法只接受这个types。 在一个静态types的语言中,编译器将捕获任何编码错误,在一个dynamictypes中你会得到一个运行时exception – 但是在任何情况下你都可以防止写入未编码的string。 匈牙利符号只是把程序员变成了一个人类检查工具,而这种工作通常是由软件来处理的。

Joel区分“匈牙利系统”和“匈牙利应用程序”,其中“匈牙利系统”编码int,float等内置types,“apps匈牙利语”编码“种类”,即更高级别的元信息关于机器types的variables,在OO或现代的函数式语言中,可以创build用户定义的types,所以在这种意义上,types和“种类”之间没有区别 – 两者都可以用types系统和“应用程序”匈牙利和匈牙利的“制度”一样多余。

因此,要回答你的问题: 系统匈牙利语只会在一个不安全,弱types的语言中有用,例如将一个浮点值分配给一个intvariables会使系统崩溃。 匈牙利语是在六十年代特别发明的,用于BCPL ,一种相当低级的语言,根本没有做任何types的检查。 我不认为今天普遍使用的任何一种语言都有这个问题,但是这个符号是作为一种货物崇拜节目而生存的。

如果您使用的是没有用户定义types的语言(如传统VBScript或早期版本的VB),则匈牙利语应用程序将有意义。 也许也是Perl和PHP的早期版本。 再次,现代语言中使用它是纯粹的货物崇拜。

在其他语言中,匈牙利只是丑陋,多余而脆弱。 它重复从types系统已知的信息, 你不应该重复自己 。 使用描述性名称来描述该types的特定实例的意图。 使用types系统来编码variables的“种类”或“类”的不变式和元信息 – 即。 types。

Joels文章的一般观点 – 错误的代码看起来是错误的 – 是一个非常好的原则。 但是,更好的防范错误的方法是,在可能的情况下,编译器会自动检测到错误的代码。

我所有的项目都使用匈牙利符号。 当我处理100个不同的标识符名称时,我发现它非常有用。

例如,当我调用一个需要一个string的函数时,我可以input“s”并命中控制空间,我的IDE会准确地显示以's'为前缀的variables名。

另外一个好处是,当我把u作为unsigned和i作为签名的ints时,我立即看到了混合的签名和未签名的方式。

我不记得在一个巨大的75000行代码库中的次数,由于命名局部variables与该类的现有成员variables一样,引起了错误(由我和其他人)。 从那以后,我总是以“m_”

它是一个品味和经验的问题。 在尝试之前不要敲它。

你忘记了包含这些信息的头号原因。 这与程序员无关。 这与你离开公司后的两三年时间里下来的人有关系。

是的,一个IDE会快速识别你的types。 但是,当你阅读一些长时间的“业务规则”代码时,最好不要在每个variables上暂停一下,找出它的types。 当我看到诸如strUserID,intProduct或guiProductID之类的东西时,它会使“加速”时间变得更容易。

我同意MS对它们的一些命名惯例做得太过分了 – 我把它归类为“太好的东西”。

命名约定是好东西,只要你坚持。 我已经经历了足够多的旧代码,这些代码让我不断地回头看看这些名字相同的variables的定义,我推着“骆驼套”(就像以前的作业一样)。 现在我正在做一个有数千行完全没有注释的VBScript代码的工作,这是一个噩梦。

在每个variables名称的开始处加上隐含的字符是不必要的,并且表明variables名称本身并不足够描述。 无论如何,大多数语言都需要声明中的variablestypes,以便信息已经可用。

还有一种情况,在维护期间,variablestypes需要改变。 例如:如果声明为“uint_16 u16foo”的variables需要成为64位无符号数,则会发生以下两种情况之一:

  1. 你会通过并更改每个variables的名称(确保不要用相同的名称来压缩任何不相关的variables),或者
  2. 只是改变types而不改变名称,这只会导致混淆。

Joel Spolsky写了一篇很好的博客文章。 http://www.joelonsoftware.com/articles/Wrong.html基本上,当一个体面的IDE会告诉你想要inputvariables的时候,如果你不记得的话,基本上不会让你的代码更难读。; 另外,如果您将代码划分得足够多,则不必记得variables声明为三页。

没有范围比键入这些天更重要,例如

 * l for local * a for argument * m for member * g for global * etc 

使用现代的重构旧代码的技术,由于改变了它的types,search和replace符号是很乏味的事情,编译器会捕获types的变化,但是经常不会错误地使用作用域,这里有明智的命名约定。

没有理由不能正确使用匈牙利符号。 这是不受欢迎的,这是因为对匈牙利符号的错误使用 (特别是在Windows API中)的长期反弹。

在糟糕的日子里,在类似于DOS的任何东西都存在于DOS之前(可能你没有足够的可用内存来运行Windows下的编译器,所以你的开发是在DOS下完成的),你没有得到任何帮助将鼠标hover在variables名称上。 (假设你有一个鼠标。)你必须处理的是事件callback函数,其中一切都以16位整数(WORD)或32位整数(LONG WORD)的forms传递给你。 然后您必须将这些参数转换为给定事件types的适当types。 实际上,大部分API几乎都是无types的。

结果是一个带有参数名称的API:

 LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 

请注意,名称wParam和lParam虽然相当糟糕,但并不比命名param1和param2更糟糕。

更糟糕的是,Window 3.0 / 3.1有近两种types的指针。 所以,例如,内存pipe理函数LocalLock的返回值是一个PVOID,但GlobalLock的返回值是一个LPVOID(长度为“L”)。 然后这个可怕的符号得到扩展,所以一个stringstring前缀lp ,以区别于一个简单的malloc'dstring。

这种事情反弹是不足为奇的。

匈牙利语符号可以在没有编译时types检查的语言中使用,因为它可以使开发人员快速提醒自己特定的variables是如何使用的。 它对性能或行为没有任何作用。 它应该提高代码的可读性,而且主要是一种品味和编码风格。 由于这个原因,它受到了许多开发者的批评 – 不是每个人都有相同的大脑布线。

对于编译时types检查语言来说,它几乎是无用的 – 向上滚动几行应该会显示声明并因此显示types。 如果全局variables或代码块跨越多个屏幕,则会遇到严重的devise和可重用性问题。 因此,其中一个批评是,匈牙利符号允许开发人员devise不好,容易摆脱它。 这可能是令人讨厌的原因之一。

另一方面,甚至编译时types检查语言也会受益于匈牙利符号 – 无效指针或win32 API中的HANDLE 。 这些混淆了实际的数据types,在那里使用匈牙利符号也许是有好处的。 然而,如果在构build时可以知道数据的types,为什么不使用适当的数据types。

一般来说,不要使用匈牙利语法。 这是一个喜欢,政策和编码风格的问题。

作为一名Python程序员,匈牙利语符号相当快地分崩离析。 在Python中,我不在乎某个string是否一个string – 我关心它是否可以一个string一样(即,如果它有一个返回string的___str___()方法)。

例如,假设我们有foo作为整数,12

 foo = 12 

匈牙利符号告诉我们,我们应该调用这个iFoo或者什么,来表示它是一个整数,所以以后我们就知道它是什么了。 除了在Python中,这是行不通的,或者说,这是没有意义的。 在Python中,我使用它时决定了我想要的types。 我想要一个string吗? 好,如果我做这样的事情:

 print "The current value of foo is %s" % foo 

请注意%s – string。 Foo不是一个string,但%运算符将调用foo.___str___()并使用结果(假设它存在)。 foo仍然是一个整数,但如果我们想要一个string,我们把它当作一个string。 如果我们想要一个浮动,那么我们把它当作一个浮动。 在像Python这样的dynamictypes语言中,匈牙利符号是毫无意义的,因为在使用它之前,什么types的东西都是无关紧要的,而且如果你需要一个特定的types,那么只要确保把它转换成这种types(例如float(foo) )当你使用它。

请注意,像PHP这样的dynamic语言没有这种好处 – PHP试图在背景中做一些“正确的事情”,这是基于几乎没有人记忆的一组难以理解的规则,这往往会导致灾难性的混乱。 在这种情况下,某种命名机制(如$files_count$file_name )可能非常方便。

在我看来,匈牙利符号就像水蛭一样。 也许在过去他们是有用的,或者至less他们似乎有用,但现在只是很多额外的打字,没有太多的好处。

IDE应该传递有用的信息。 匈牙利人可能已经做了一些(不是很多,但一些),当IDE是不太先进的感觉。

应用程序匈牙利语对我来说是希腊语 – 好的方法

作为一名工程师,不是一名程序员,我立即拿起Joel关于Apps Hungarian的优点的文章: “让错误的代码看起来不对” 。 我喜欢匈牙利应用程序,因为它模仿工程,科学和math如何使用子和超级脚本符号 (如希腊字母,math运算符等) 来表示方程和公式 。 举一个牛顿的万有引力定律的例子:首先用标准的math符号,然后用Apps匈牙利的伪代码:

牛顿的地球和火星的万有引力定律

 frcGravityEarthMars = G * massEarth * massMars / norm(posEarth - posMars) 

在math符号中,最突出的符号是代表存储在variables中的那些信息的那些符号:力,质量,位置vector等等。下标是次要的,以澄清:什么位置? 这正是匈牙利应用程序所做的。 它告诉你存储在variables中的事物,然后进入具体的事情 – 关于最接近的代码可以得到math符号。

明显的强types可以解决Joel的文章中安全与不安全的string例子,但是你不会为位置和速度向量定义不同的types; 两者都是三个大小的双数组,任何你可能对一个人做的事情都可能适用于另一个。 此外,连接位置和速度(做一个状态向量)或者取其点积是非常有意义的,但可能不会添加它们。 如何打字让前两个,禁止第二,这样一个系统将如何延伸到每一个可能的操作,你可能要保护? 除非你愿意在你的打字系统中编码所有的math和物理。

最重要的是,很多工程都是用Matlab这样的弱types的高级语言,或者像Fortran 77或Ada这样的老式语言来完成的。

所以,如果你有一个花哨的语言和IDE和应用程序匈牙利不帮你,然后忘了它 – 很多人显然有。 但是对于我来说,比使用弱types或dynamictypes的语言的新手程序员更糟,我可以使用匈牙利语应用程序更快地编写更好的代码。

这是非常多余的,没用的是最现代化的IDE,他们在这方面做得很好。

加上 – 对我来说 – 这只是讨厌看intI,strUserName等:)

如果我觉得有用的信息正在传递,为什么我不能把它放在那里?

那么谁在乎别人的想法呢? 如果您觉得有用,请使用符号。

我的经验,这是不好的,因为:

1 – 那么如果你需要改变一个variables的types(例如,如果你需要将一个32位整数扩展到一个64位整数),那么你打破了所有的代码;

2 – 这是无用的信息,因为这个types已经在声明中了,或者你使用了一个dynamic语言,其中实际的types不应该如此重要。

而且,用一种接受generics编程的语言(即,当您编写函数时某些variables的types不确定的函数),或者使用dynamictypes系统(即在编译时甚至不确定types时),您将如何命名variables? 而且大多数现代语言都支持一种或另一种语言,即使是有限制的forms。

在Joel Spolsky制作错误的代码看起来错了,他解释说,大家认为匈牙利符号(他称匈牙利系统)不是什么真正意图成为的(他称之为“匈牙利应用程序”)。 向下滚动到我是匈牙利标题看到这个讨论。

基本上,系统匈牙利是毫无价值的。 它只是告诉你同样的事情你的编译器和/或IDE会告诉你。

Apps匈牙利语告诉你这个variables应该是什么意思,而且实际上可能是有用的。

我一直认为,在正确的地方前缀或两个不会伤害。 我想如果我能传授一些有用的东西,比如“嘿,这是一个接口,不要指望具体的行为”,就像在IEnumerable中那样,我应该去做。 评论可以凌乱不止一个或两个字符符号。

如果控件列表显示在IDE中的按字母顺序排列的下拉列表中,那么这对于命名窗体上的控件(btnOK,txtLastName等)是一个有用的约定。

我倾向于只使用匈牙利符号与ASP.NET服务器控件,否则我觉得很难找出什么控件是什么forms。

以下面的代码片段:

 <asp:Label ID="lblFirstName" runat="server" Text="First Name" /> <asp:TextBox ID="txtFirstName" runat="server" /> <asp:RequiredFieldValidator ID="rfvFirstName" runat="server" ... /> 

如果有人可以更好地显示没有匈牙利语的控制名称,我会试着转向它。

乔尔的文章是伟大的,但似乎省略了一个重点:

匈牙利人在整个代码库中提供了一个特定的“想法”(种类+标识符名称),甚至是一个非常大的代码库。

这对代码维护非常重要。 这意味着你可以使用好的单行文本search(grep,findstr,在所有文件中查找)来查找每一个提及的“想法”。

当我们知道如何阅读代码的IDE时,为什么这很重要? 因为他们还不是很好。 这很难在一个小的代码库中看到,但很明显在一个大的代码库中,当可能在注释,XML文件,Perl脚本以及源代码控制之外的地方(文档,维基,错误数据库)中提到“想法”。

即使在这里,你也必须小心谨慎 – 例如C / C ++macros中的标记粘贴可以隐藏标识符的提及。 这种情况可以使用编码约定来处理,无论如何它们往往只影响代码库中的less数标识符。

PS关于使用types系统与匈牙利语 – 最好是使用两者。 你只需要错误的代码来看看错误,如果编译器不会为你捕捉它。 有很多情况下,编译器不能捕获它。 但是,哪里可行 – 是的,请做的,而不是!

但是,在考虑可行性时,要考虑分裂types的负面影响。 例如在C#中,使用非内置types包装“int”会产生巨大的后果。 So it makes sense in some situations, but not in all of them.

Debunking the benefits of Hungarian Notation

  • It provides a way of distinguishing variables.

If the type is all that distinguishes the one value from another, then it can only be for the conversion of one type to another. If you have the same value that is being converted between types, chances are you should be doing this in a function dedicated to conversion. (I have seen hungarianed VB6 leftovers use strings on all of their method parameters simply because they could not figure out how to deserialize a JSON object, or properly comprehend how to declare or use nullable types. ) If you have two variables distinguished only by the Hungarian prefix, and they are not a conversion from one to the other, then you need to elaborate on your intention with them.

  • It makes the code more readable.

I have found that Hungarian notation makes people lazy with their variable names. They have something to distinguish it by, and they feel no need to elaborate to its purpose. This is what you will typically find in Hungarian notated code vs. modern: sSQL vs. groupSelectSql ( or usually no sSQL at all because they are supposed to be using the ORM that was put in by earlier developers. ), sValue vs. formCollectionValue ( or usually no sValue either, because they happen to be in MVC and should be using its model binding features ), sType vs. publishSource, etc.

It can't be readability. I see more sTemp1, sTemp2… sTempN from any given hungarianed VB6 leftover than everybody else combined.

  • It prevents errors.

This would be by virtue of number 2, which is false.

In the words of the master:

http://www.joelonsoftware.com/articles/Wrong.html

An interesting reading, as usual.

Extracts:

"Somebody, somewhere, read Simonyi's paper, where he used the word “type,” and thought he meant type, like class, like in a type system, like the type checking that the compiler does. He did not. He explained very carefully exactly what he meant by the word “type,” but it didn't help. The damage was done."

"But there's still a tremendous amount of value to Apps Hungarian, in that it increases collocation in code, which makes the code easier to read, write, debug, and maintain, and, most importantly, it makes wrong code look wrong."

Make sure you have some time before reading Joel On Software. 🙂

几个原因:

  • Any modern IDE will give you the variable type by simply hovering your mouse over the variable.
  • Most type names are way long (think HttpClientRequestProvider ) to be reasonably used as prefix.
  • The type information does not carry the right information, it is just paraphrasing the variable declaration, instead of outlining the purpose of the variable (think myInteger vs. pageSize ).

I don't think everyone is rabidly against it. In languages without static types, it's pretty useful. I definitely prefer it when it's used to give information that is not already in the type. Like in C, char * szName says that the variable will refer to a null terminated string — that's not implicit in char* — of course, a typedef would also help.

Joel had a great article on using hungarian to tell if a variable was HTML encoded or not:

http://www.joelonsoftware.com/articles/Wrong.html

Anyway, I tend to dislike Hungarian when it's used to impart information I already know.

Of course when 99% of programmers agree on something, there is something wrong. The reason they agree here is because most of them have never used Hungarian notation correctly.

For a detailed argument, I refer you to a blog post I have made on the subject.

http://codingthriller.blogspot.com/2007/11/rediscovering-hungarian-notation.html

I started coding pretty much the about the time Hungarian notation was invented and the first time I was forced to use it on a project I hated it.

After a while I realised that when it was done properly it did actually help and these days I love it.

But like all things good, it has to be learnt and understood and to do it properly takes time.

The Hungarian notation was abused, particularly by Microsoft, leading to prefixes longer than the variable name, and showing it is quite rigid, particularly when you change the types (the infamous lparam/wparam, of different type/size in Win16, identical in Win32).

Thus, both due to this abuse, and its use by M$, it was put down as useless.

At my work, we code in Java, but the founder cames from MFC world, so use similar code style (aligned braces, I like this!, capitals to method names, I am used to that, prefix like m_ to class members (fields), s_ to static members, etc.).

And they said all variables should have a prefix showing its type (eg. a BufferedReader is named brData). Which shown as being a bad idea, as the types can change but the names doesn't follow, or coders are not consistent in the use of these prefixes (I even see aBuffer, theProxy, etc.!).

Personally, I chose for a few prefixes that I find useful, the most important being b to prefix boolean variables, as they are the only ones where I allow syntax like if (bVar) (no use of autocast of some values to true or false). When I coded in C, I used a prefix for variables allocated with malloc, as a reminder it should be freed later. 等等。

So, basically, I don't reject this notation as a whole, but took what seems fitting for my needs.
And of course, when contributing to some project (work, open source), I just use the conventions in place!