为什么不应该使用"匈牙利表示法"?
我知道匈牙利语是指提供有关变量,参数或者类型的信息作为其名称的前缀。每个人似乎都在疯狂地反对它,即使在某些情况下,这似乎是一个好主意。如果我觉得正在传递有用的信息,为什么不把它放在可用的地方呢?
另请参阅:人们在现实世界中是否使用匈牙利命名约定?
解决方案
我认为它会使源代码变得混乱不堪。
使用强类型语言也不会给我们带来什么好处。如果我们进行任何形式的类型不匹配伪造,编译器都会告诉我们。
IDE应该提供有用的信息。当IDE不够先进时,匈牙利人可能会产生某种意义(不是很多,而是某种意义)。
乔尔·斯波斯基(Joel Spolsky)撰写了一篇很好的博客文章。
http://www.joelonsoftware.com/articles/Wrong.html
基本上可以归结为,当一个体面的IDE告诉我们要键入变量时,如果我们不记得了,就不会使代码难以阅读。同样,如果我们对代码进行足够的分隔,则不必记住将变量声明为三页以上的内容。
- 他们是巨大的眼神
- IDE应该能够告诉我们所有有关变量类型的知识
- 良好的名称(HN会妨碍我们)应该向我们传达我们需要了解的有关变量的所有其他信息。
我一直认为,在正确的位置添加一个或者两个前缀不会有问题。我认为,如果我可以传递一些有用的信息,例如"嘿,这是一个接口,不要指望特定的行为",就像在IEnumerable中一样,我应该这样做。注释不仅会使一两个字符符号变得混乱,还会使事情变得混乱。
对于大多数现代IDE来说,它具有令人难以置信的冗余性和无用性,它们在使类型明显化方面做得很好。
另外-对我-看到intI,strUserName等很烦人:)
我不认为每个人都对此表示强烈反对。在没有静态类型的语言中,它非常有用。当用于提供类型中尚未提供的信息时,我绝对喜欢它。像在C中一样,char * szName表示该变量将引用以null终止的字符串-在char *中不是隐式的-当然,typedef也将有所帮助。
Joel撰写了一篇很棒的文章,介绍了如何使用匈牙利语来判断变量是否经过HTML编码:
http://www.joelonsoftware.com/articles/Wrong.html
无论如何,当匈牙利人用来传递我已经知道的信息时,我往往会不喜欢它。
用大师的话说:
http://www.joelonsoftware.com/articles/Wrong.html
像往常一样有趣的阅读。
提取物:
"有人在某个地方读了Simonyis的论文,他在其中使用了类型一词,并认为他的意思是像类一样,在类系统中,就像在编译器中进行类型检查一样。他没有。他非常仔细地解释了到底他的意思是"类型",但并没有帮助。损害已经造成。"
"但是,Apps Hungarian仍然具有巨大的价值,因为它增加了代码的并置,这使得代码更易于阅读,编写,调试和维护,并且最重要的是,它使错误的代码看起来不正确。"
在阅读Joel On Software之前,请确保我们有一些时间。 :)
If I feel that useful information is being imparted, why shouldn't I put it right there where it's available?
那么,谁在乎别人的想法呢?如果我们发现它有用,请使用表示法。
我的经验很糟糕,因为:
1然后,如果需要更改变量的类型(即,需要将32位整数扩展为64位整数),则会破坏所有代码。
2这是无用的信息,因为类型已经在声明中,或者我们使用动态语言,而实际的类型一开始就不那么重要。
此外,对于接受通用编程的语言(即,在编写函数时不确定某些变量的类型的函数)或者动态类型系统(即,在编译时甚至无法确定类型的函数),我们将如何命名变量?而且大多数现代语言都支持一种或者另一种语言,即使是受限制的形式也是如此。
大多数人以错误的方式使用匈牙利表示法,并且得到错误的结果。
阅读Joel Spolsky撰写的精彩文章:使错误的代码看起来错误。
简而言之,在匈牙利符号中用"类型"(字符串)(系统匈牙利语)作为变量名的前缀是不好的,因为它没有用。
匈牙利注释法,正如它的作者所打算的那样,在变量名前添加了它的"种类"(使用Joel的示例:安全字符串或者不安全字符串),因此所谓的Apps Hungarian具有其用途,并且仍然有价值。
几个原因:
- 任何现代的IDE都可以通过将鼠标悬停在变量上来为我们提供变量类型。
- 大多数类型名称都相当长(认为HttpClientRequestProvider),可以合理地用作前缀。
- 类型信息没有正确的信息,只是对变量声明进行了解释,而不是概述变量的用途(请考虑myInteger与pageSize)。
另请参见成员变量使用哪种前缀?
匈牙利语表示法在不进行编译时类型检查的语言中很有用,因为它可使开发人员快速提醒自己如何使用特定变量。它对性能或者行为没有任何作用。它应该提高代码的可读性,并且主要是一种口味和编码风格。因此,许多开发人员都批评它-并非每个人的大脑都有相同的连线。
对于编译时的类型检查语言,大多数情况下是没有用的-向上滚动几行应显示该声明,然后键入。如果全局变量或者代码块跨越多个屏幕,那么我们将面临严重的设计和可重用性问题。因此,批评之一是匈牙利符号法使开发人员的设计不好,并容易摆脱它。这可能是仇恨的原因之一。
另一方面,在某些情况下,甚至编译时类型检查语言也会受益于匈牙利符号-win32 API中的空指针或者HANDLE。这些混淆了实际的数据类型,并且在此处使用匈牙利表示法可能会有好处。但是,如果在构建时就可以知道数据的类型,为什么不使用适当的数据类型呢?
通常,没有硬性理由不使用匈牙利表示法。这是由喜好,政策和编码风格决定的。
不需要在每个变量名的开头加上隐含的字符,这表明变量名本身不够描述性。大多数语言无论如何都需要在声明时使用变量类型,因此信息已经可用。
还有在维护期间需要更改变量类型的情况。示例:如果声明为" uint_16 u16foo"的变量需要变为64位无符号,则将发生以下两种情况之一:
- 我们将仔细检查并更改每个变量的名称(确保不要用相同的名称命名任何不相关的变量),或者
- 只需更改类型而不更改名称,这只会造成混乱。
在Joel Spolsky的"使代码看起来错误"中,他解释说每个人都认为匈牙利符号(他称为"系统匈牙利语")并不是它的真正意图(他称为Apps Hungarian)。向下滚动至Im Im标题以查看此讨论。
基本上,系统匈牙利人一文不值。它只是告诉我们编译器和/或者IDE会告诉相同内容。
Apps Hungarian会告诉我们该变量的含义,并且实际上可能有用。
匈牙利表示法被滥用,特别是被Microsoft滥用,导致前缀长于变量名,并且显示出它很僵化,尤其是当我们更改类型(臭名昭著的lparam / wparam,在Win16中具有不同的类型/大小,在Win32中相同)。
因此,无论是由于这种滥用,还是由于它被M $使用,都被认为是无用的。
在我的工作中,我们使用Java编写代码,但是创始人来自MFC世界,因此请使用类似的代码样式(大括号,我喜欢!),大写的是方法名称,我习惯了,在类成员(字段,例如m_)前加前缀),s_到静态成员等)。
他们说所有变量都应该有一个前缀来显示其类型(例如,一个BufferedReader名为brData)。这是一个坏主意,因为类型可以更改,但名称不能跟随,或者编码器在使用这些前缀时不一致(我什至看到aBuffer,theProxy等!)。
就我个人而言,我选择了一些我认为有用的前缀,最重要的是用b前缀布尔变量,因为它们是我允许使用诸如if(bVar)之类的语法的唯一变量(不使用将某些值自动转换为true)或者false)。
当我用C语言编写代码时,我为malloc分配的变量使用了前缀,以提醒注意,以后应将其释放。等等。
因此,基本上,我总体上不拒绝这种表示法,而是选择了适合我需要的内容。
当然,当为某个项目(工作,开源)做出贡献时,我只是使用了约定!
匈牙利语表示法仅在没有用户定义类型的语言中才有意义。在现代功能或者OO语言中,我们将有关值的"种类"的信息编码为数据类型或者类,而不是变量名。
几个答案参考Joels的文章。但是请注意,他的示例在VBScript中,它不支持用户定义的类(至少很长一段时间)。在具有用户定义类型的语言中,我们可以通过创建HtmlEncodedString-type然后让Write方法仅接受它来解决相同的问题。在静态类型的语言中,编译器将捕获任何编码错误,在动态类型的语言中,我们将获得运行时异常,但是无论如何,都可以防止编写未编码的字符串。匈牙利语表示法只是将程序员转变为人工检查人员,而这种工作通常可以通过软件更好地处理。
Joel区分"系统匈牙利语"和"应用程序匈牙利语",其中"系统匈牙利语"编码int,float等内置类型,而"应用程序匈牙利语"编码"种类",这是高级元信息关于机器类型周围的变量,在OO或者现代功能语言中,我们可以创建用户定义的类型,因此类型和"种类"之间没有区别,在这种意义上,两者都可以由类型系统表示," apps"匈牙利语是和"系统"匈牙利一样多余。
因此,请回答问题:系统匈牙利语仅在不安全,弱类型的语言(例如将浮点值分配给int变量将使系统崩溃。匈牙利表示法是60年代专门发明的,用于BCPL,这是一种非常底层的语言,根本不进行任何类型检查。我不认为当今任何通用语言都存在此问题,但这种符号作为一种"货物崇拜"编程而存在。
如果我们使用的语言没有用户定义的类型(例如旧版VBScript或者VB的早期版本),那么匈牙利语的应用程序就有意义。也许还有Perl和PHP的早期版本。再次,在现代语言中使用它是纯粹的货物崇拜。
用任何其他语言,匈牙利人都是丑陋,多余和脆弱的。它重复了类型系统中已知的信息,因此我们不应重复自己。为变量使用描述性名称,以描述此特定类型实例的意图。使用类型系统对变量的"种类"或者"类"编码不变式和元信息。类型。
Joels文章的一般要点是使代码看起来错误是一个很好的原则。但是,更好的防范错误的方法是,在所有可能的情况下,编译器都会自动检测到错误的代码。
使用匈牙利语注释vmake使ncode难以理解。
匈牙利语是不好的,因为它将宝贵的字符从变量名中夺走,以换取一些类型信息?
首先,使用强类型语言,如果我们进行任何真正的愚蠢操作,编译器都会警告我们。
其次,如果我们相信良好的模块化代码并且在任何一个函数中都没有做太多的工作,那么我们很可能在变量始终被使用的代码上方声明了变量(因此,我们在那里拥有类型)。
第三,如果给每个指针加上p前缀,给每个类加上C前缀,则实际上会破坏现代IDE的智能感知能力(我们知道该功能会在我们键入什么类名以及输入它得到它时立即猜测)是的,如果在每个类的前面都加上C,则总是至少要键入1个额外的字母)。
我认为整个美学方面都被夸大了。如果那是最重要的事情,我们将不称自己为开发人员,而是称呼平面设计师。
我认为,重要的一部分是我们描述对象角色是什么,而不是角色。我们不会称自己为HumanDustman,因为在其他情况下,我们最重要的不是成为人类。
对于重构目的,这也非常重要:
public string stringUniqueKey = "ABC-12345";
如果我们决定使用GUID而不是字符串怎么办,则在重构所有引用代码之后,变量名看起来会很愚蠢。
或者:
public int intAge = 20;
将其更改为浮点数,我们将遇到相同的问题。等等。
没有理由不应该正确使用匈牙利符号。之所以不受欢迎,是因为长期以来有人反对使用匈牙利符号,特别是在Windows API中,它们之间存在着强烈的反冲。
在糟糕的日子里,在为DOS提供类似IDE的东西之前(奇怪的是,我们没有足够的可用内存来在Windows下运行编译器,因此开发是在DOS上完成的),我们没有得到任何帮助。将鼠标悬停在变量名上。 (假设我们有鼠标。)我们需要处理的是事件回调函数,其中所有内容都以16位int(WORD)或者32位int(LONG WORD)的形式传递给我们。然后,我们必须将那些参数转换为给定事件类型的适当类型。实际上,许多API实际上都是无类型的。
结果是,API的参数名称如下:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
请注意,名称wParam和lParam虽然很糟糕,但实际上并不比命名它们为param1和param2更糟。
更糟糕的是,Window 3.0 / 3.1具有两种类型的指针,near和far。因此,例如,内存管理功能LocalLock的返回值是PVOID,而GlobalLock的返回值是LPVOID(长时间带有" L")。然后扩展了这种可怕的表示法,以便将一个长指针字符串作为lp前缀,以区别于简单地被malloc分配的字符串。
反对这种事情也就不足为奇了。
如果控件列表显示在IDE中的字母下拉列表中,则这对于在表单(btnOK,txtLastName等)上命名控件很有用。
乔尔错了,这就是原因。
他正在谈论的"应用程序"信息应该在类型系统中进行编码。我们不应该依赖于翻转变量名来确保我们不会将不安全的数据传递给需要安全数据的函数。我们应该将其设置为类型错误,这样就不可能这样做。任何不安全的数据都应具有标记为"不安全"的类型,以便根本无法将其传递给安全功能。要从不安全状态转换为安全状态,需要使用某种消毒功能进行处理。
乔尔所说的"种类"很多东西都不是种类。实际上,它们是类型。
但是,大多数语言所缺乏的是一种类型表达系统,足以表现出这种区别。例如,如果C具有一种"强类型定义"(其中类型定义名称具有基本类型的所有操作,但不能转换为基本类型),则很多此类问题都将消失。例如,如果我们可以说" strong typedef std :: string unsafe_string;"来引入新类型" unsafe_string",则该类型不能转换为std :: string(因此可能参与重载解析等)。那么我们就不需要傻的前缀。
因此,关于匈牙利语适用于非类型事物的中心说法是错误的。它用于类型信息。当然,类型信息比传统的C类型信息更丰富;类型信息对某种语义细节进行编码,以指示对象的用途。但是它仍然是类型信息,正确的解决方案始终是将其编码到类型系统中。将其编码到类型系统中无疑是获得正确验证和执行规则的最佳方法。变量名称根本不会榨菜。
换句话说,目标不应是"使错误的代码对开发人员来说看起来是错误的"。应该是"使错误的代码对编译器看起来不正确"。
我在所有项目中始终使用匈牙利表示法。当我处理100个不同的标识符名称时,我发现它真的很有帮助。
例如,当我调用一个需要字符串的函数时,我可以键入's'并点击control-space,而我的IDE会准确地向我显示以's'为前缀的变量名。
另一个优点是,当我给u加上unsigned前缀而给i加上带符号的ints前缀时,我立即看到以潜在危险的方式将有符号和无符号混合在一起的地方。
我记不清在一个75,000行代码库中由于将局部变量命名为与该类的现有成员变量相同而导致的错误(也是由我和其他人引起)的次数。从那时起,我总是在成员前加上'm_'
这是口味和经验的问题。尝试之前,请勿敲它。
如今,范围是否比输入更为重要,例如
* l for local * a for argument * m for member * g for global * etc
使用现代的重构旧代码,搜索和替换符号的技术,因为我们更改了它的类型很乏味,因此编译器将捕获类型更改,但通常不会捕获对范围的不正确使用,明智的命名约定在这里有所帮助。
我们忘记了包括此信息的第一原因。程序员与我们无关。它与离开公司2或者3年后要阅读相关内容的人有关。
是的,IDE将为我们快速识别类型。但是,当我们阅读大量"业务规则"代码时,不必停在每个变量上以找出其类型是一件很不错的事情。当我看到诸如strUserID,intProduct或者guiProductID之类的内容时,这将使"启动"时间变得更加容易。
我同意MS在某些命名约定方面走得太远了,我将其归类为"太多了"。
只要我们遵守命名约定,它们就是好东西。我已经经历了足够多的旧代码,使我不断地回头查看许多如此命名的变量的定义,以至于我推了"骆驼套"(在上一份工作中被称为)。现在,我从事的工作是使用VBScript编写成千上万行完全未经注释的经典ASP代码,这是一场噩梦,试图弄清一切。
当然,当99%的程序员就某件事达成共识时,就有错了。他们之所以同意这一点,是因为他们大多数人从未正确使用匈牙利符号。
有关详细的论点,请参考我在该主题上发表的一篇博客文章。
http://codingthriller.blogspot.com/2007/11/rediscovering-hungarian-notation.html
大约在发明匈牙利表示法的那一刻,我就开始编写代码了,这是我第一次被迫在讨厌它的项目中使用它。
过了一会儿,我意识到正确地完成它确实可以帮上忙,这些天我很喜欢它。
但是,就像所有美好的事物一样,必须学习和理解它,并且正确地进行操作需要时间。
作为Python程序员,匈牙利符号法很快就瓦解了。在Python中,我不在乎某个东西是否是字符串,我不在乎它是否可以像字符串一样工作(即,是否有返回字符串的`___ str ___())方法)。
例如,假设我们将foo作为一个整数,即12
foo = 12
匈牙利表示法告诉我们应该将其称为iFoo之类的东西,以表示它是整数,以便以后我们知道它是什么。除了在Python中,这是行不通的,或者说,这没有任何意义。在Python中,我可以决定使用哪种类型。我想要一个字符串吗?好吧,如果我做这样的事情:
print "The current value of foo is %s" % foo
注意"%s"字符串。 Foo不是字符串,但是%
运算符将调用foo .___ str ___()
并使用结果(假设它存在)。 foo仍然是整数,但是如果需要字符串,我们将其视为字符串。如果我们想要浮点数,则将其视为浮点数。在像Python这样的动态类型语言中,匈牙利表示法是没有意义的,因为在使用某种东西之前,它与什么类型无关紧要;如果需要特定类型,则只需确保将其强制转换为该类型即可(例如,float( foo)
)。
请注意,动态语言(如PHP)没有此好处,PHP会根据一套几乎没人记住的模糊规则,在后台尝试做"正确的事情",这通常会导致灾难性的混乱。在这种情况下,可以使用诸如$ files_count`或者$ file_name之类的命名机制。
在我看来,匈牙利表示法就像水ches。也许在过去它们很有用,或者至少看起来有用,但是如今,它只是带来了很多额外的好处而已。
我倾向于仅将匈牙利表示法与ASP.NET服务器控件一起使用,否则我很难确定表单上的控件是什么。
请看以下代码片段:
<asp:Label ID="lblFirstName" runat="server" Text="First Name" /> <asp:TextBox ID="txtFirstName" runat="server" /> <asp:RequiredFieldValidator ID="rfvFirstName" runat="server" ... />
如果有人可以显示一种更好的方式来获得一组没有匈牙利语的控件名称,我很想转向它。
我找不到链接,但是我记得读过某个地方(我同意),避免匈牙利符号会带来更好的编程风格。
在编写程序语句时,在调用其方法之前,我们不应该考虑"此对象的类型",而应该考虑"我要对该对象做什么","向其发送哪条消息" "。
有点模糊的概念可以解释,但我认为它是可行的。
例如,如果客户名存储在变量customerName中,则不必关心它是字符串还是其他类。考虑此对象的功能更重要。我们是否希望它进行print(),getFirstName(),getLastName(),convertToString()等。一旦将其作为String类的实例并视为已授予,就限制了自己和设计,因为我们必须构建所有我们在代码中其他地方需要的其他逻辑。
多年以来,我在编程中都使用匈牙利表示法。除了一些视觉混乱和更改数据类型时更改前缀的任务外,没有人可以说服我。直到最近-当我不得不在同一解决方案中组合现有Cand VB.NET程序集时。
结果:我必须将" fltSomeVariable"传递给" sngSomeVariable"方法参数。即使是同时在两个Cand VB.NET中进行编程的人,它也让我措手不及,让我停了一会儿。 (Cand VB.NET有时使用不同的名称来表示相同的数据类型,例如,浮点型和单一型。)
现在考虑一下:如果创建一个可以从多种语言中调用的COM组件怎么办?对于.NET程序员而言,VB.NET和C的"转换"非常容易。但是用C ++或者Java开发的人呢?对于不熟悉C ++的.NET开发人员来说," dwSomeVariable"是否有意义?
如果我们在不被告知的情况下不知道变量的类型,那么我们可能无论如何都不应该弄乱它
类型也可能不那么重要。如果我们知道方法的作用,则可以弄清楚变量的作用,然后我们将了解程序的作用
有时候我们会想要它。当类型很重要且声明不近或者无法轻松推断类型时。但这绝不应该被视为绝对