49

我从来都不是匈牙利符号的粉丝,除非你在做一些非常低级的编程,否则我一直觉得它毫无用处,但是在我从事的每个 C++ 项目中,都强制执行了某种匈牙利符号策略,并且使用一些“不是真正的匈牙利语”前缀作为字段的 m_,静态的 s_,全局的 g_ 等等。

很快我就意识到它在 C# 中是多么无用,并逐渐开始放弃我所有的旧习惯......但是'm_'的东西。我仍然在私有字段上使用 m_ 前缀,因为我真的发现能够区分参数、本地和字段非常有用。

MSDN 上字段页面的命名约定说我不应该,但没有说明原因(例如,Google 的约定通常倾向于合理化他们的规定)。

是否有我不应该这样做的原因,或者这只是风格问题。如果是后者,前缀通常被认为是一种不好的风格吗?我可以期待其他代码库工作人员的负面反应吗?

4

25 回答 25

52

我喜欢成员字段的下划线前缀。大多数情况下,我喜欢它,因为这样,我的所有成员字段都按字母顺序显示在屏幕顶部向导栏中的方法之前。

向导栏

于 2009-03-18T18:51:49.097 回答
41

当你应该:

  • 当你的项目编码指南说你应该

当你不应该:

  • 当你的项目编码指南说你不应该

如果您还没有任何指导方针,您可以自由选择您或您的团队想要并感觉最舒服的任何内容。就我个人而言,在编写 C++ 代码时,我倾向于m_为成员使用,它确实有帮助。当用其他语言编码时,尤其是那些没有真正类的语言(如 Javascript、Lua)我不会。

简而言之,我不相信有“正确”和“错误”的方式。

于 2009-03-18T18:35:04.143 回答
21

C# 3.0 中的自动实现属性特性以一种或另一种方式减少了对这种约定的需求。而不是写

string m_name;
public string Name { get { return m_name; } }

或者

string _Name;
public string Name { get { return _Name; } }

(或任何其他约定),您现在可以编写

public string Name { get; private set; }

由于您不再需要显式的后备存储变量,因此您不再需要为其命名;从而避免了整个讨论。

显然,当您确实需要显式后备存储(例如执行验证)时,此参数不适用。

于 2009-03-18T18:38:55.837 回答
11

正如一些人所暗示的那样,MS 指南说:

不要为字段名称使用前缀。例如,不要使用 g_ 或 s_ 来区分静态字段和非静态字段。

我碰巧同意这一点。前缀使您的代码看起来很丑陋,并用无关紧要的字符浪费空间。话虽如此,通常使用字段来支持字段和属性具有相同名称的属性(私有字段为驼峰大小写,属性为帕斯卡大小写)。在 VB 中,这不起作用,因为 VB 不区分大小写。在这种情况下,我建议使用单个 _ 前缀。不多也不少。它看起来更干净,恕我直言。

于 2009-03-18T18:53:05.160 回答
11

我已经尝试过 m_、s_,只是 _,根本没有前缀。我已经决定只对所有静态和实例变量使用 _。我认为区分静态变量和实例变量并不重要。理论上听起来不错,实际上它不会产生问题。

一位同事曾经提出了一个令人信服的论点来消除所有前缀,我们在一个项目上进行了尝试,它的效果比我预期的要好。我把它带到了我的下一个项目,并对它“干扰”了 Intellisense 感到恼火。当您有以下情况时

int foo;
public int Foo
{
  get { return foo; }
}

开始键入 foo 将建议实例变量和属性。用下划线前缀变量消除了烦人的双重建议,所以我切换回只使用_。

于 2009-03-18T19:24:17.847 回答
10

我尝试遵循MSDN .NET 库指南。它们包括一个命名指南部分。

显然,这些是您的项目指南的次要内容。

于 2009-03-18T18:37:05.340 回答
10

我更喜欢用下划线而不是“m”来标记属性支持字段(尽管正如已经提到的 .NET 3.0+ 减少了自动属性的需要)。一方面,当我开始使用它们时,它会将它们放在 InteliSense 列表的顶部。

我承认我需要重新了解 MSDN 上的指南,这些天事情变化得如此之快。

于 2009-03-18T18:48:22.410 回答
10

使用像 resharper 这样的工具,真的没有理由使用前缀。此外,如果您编写简短的方法,您应该能够非常快速地判断 var 的来源。最后,我想我不会真正看到区分静态与否的必要性,因为如果您尝试做一些您无法做的事情,resharper 会再次将其标记为红线。即使没有重新锐化器,您也可能会被编译器保存。

于 2009-03-18T18:50:31.500 回答
6

出于与您陈述的相同原因,我总是在成员变量前面加上m_和在静态变量前面加上s_ 。有些人用下划线作为成员变量的前缀,但我总是觉得这看起来有点奇怪(但这只是个人喜好)。

与我一起工作的大多数人都使用 m_/s_ 前缀。我真的认为你使用什么并不重要,只要你保持一致。

于 2009-03-18T18:36:12.957 回答
5

我从不使用它们。它鼓励草率的编码。MSDN 编码指南,就是它所在的地方。

于 2009-03-18T18:41:02.100 回答
4

正如@John Kraft 提到的,没有“正确”的答案。MattJ 是最接近的——您应该始终遵循公司的风格指南。在罗马的时候,以及所有这些。

至于我个人的看法,既然这里需要,我投票你m_完全放弃。

我相信最好的风格是所有成员都是PascalCased,无论可见性如何(这意味着甚至private成员),并且所有论点都是camelCased. 我不会打破这种风格。

我可以理解为属性后备存储字段添加前缀的愿望;毕竟你必须区分字段和属性,对吧?我同意,你必须。但是使用后修复。

使用. m_MyProperty_ _MyProperty_ MyPropertyValue它更容易阅读和理解,而且——更重要的是——它与智能感知中的原始属性名称接近。

最终,这就是我更喜欢后缀的原因。如果我想MyPropertyValue使用智能感知访问,你(通常)输入“ My <down-arrow> <tab>”,因为那时你已经足够接近MyProperty并且MyPropertyValue在列表中。如果你想m_MyProperty使用智能感知访问,你必须输入“ m_My <tab>”。

在我看来,这是关于击键经济的。

于 2009-03-18T19:08:54.127 回答
4

以下是使用 _(而不是 m_)的几个原因。

(1) 尽管有 MS 的命名指南,但许多 BCL 人还是这样做了。(查看他们的博客。)这些人编写了框架,所以他们有一些值得效仿的好习惯。MSDN 上一些最有用的示例代码是由他们编写的,因此使用下划线约定。这是一个事实上的行业标准。

(2) 单个下划线是一种明显但不显眼的方式,通过简单地阅读源代码来消除方法和类级变量的歧义。它可以帮助人们在阅读新(或旧)代码时一目了然。是的,您可以将鼠标悬停在 IDE 中查看它,但我们不应该被迫这样做。您可能想在文本编辑器中阅读它,或者我敢说,在纸上。

(3) 有人说您不需要任何前缀,因为方法会很短,稍后如果需要,您可以将字段更改为自动实现的属性。但在现实世界中,方法只要需要就可以,并且字段和属性之间存在重要差异(例如序列化和初始化)。

脚注: m_ 中成员的“m”在我们这里的使用中是多余的,但它是小写的,因为许多旧命名约定中的一个想法是类型名称以大写开头,而实例名称以小写开头。这不适用于.NET,所以它是双重冗余的。此外,匈牙利表示法有时对旧的 C 编译器很有用(例如整数或指针转换和算术),但即使在 C++ 中,它在处理类时也没有用处。

于 2010-05-03T23:37:25.050 回答
3

我习惯的是私有属性有小下划线 f.ex“字符串_名称”。公众得到了“名字”。并且方法中的输入变量得到小写字母“void MyMethod(string name)”。

如果你有 static const 通常用大写字母写。static const MYCONST = "hmpf".

于 2009-03-18T18:47:43.053 回答
3

C++ 和 C# 之间有一个重要区别:工具支持。当您遵循既定指南(或常见变体)时,您将获得 C++ 从未有过的深度工具支持。遵循标准允许工具进行比您原本能够做的更深入的重构/重命名操作。Resharper 做到了这一点。所以坚持使用既定标准之一。

于 2009-03-18T18:47:50.510 回答
3

我从不这样做,原因是我[试图]保持我的方法简短。如果我可以在屏幕上看到整个方法,我可以看到参数,我可以看到本地,所以我可以知道类拥有什么,什么是参数或本地。

我通常会使用特定的符号来命名我的参数和本地变量,但并非总是如此。如果不是不一致,我什么都不是。我依赖于我的方法很短这一事实,并试图阻止他们在应该只做 X 的时候做 X、Y 和 Z。

无论如何,这是我的两分钱。

于 2009-03-18T19:09:59.630 回答
3

我相信我会因此而受到抨击,但就这样吧。

它被称为 Microsoft 的 .NET 库指南,但实际上是Brad Abrams 的观点(在此处记录)——还有其他有正当理由的观点。

人们倾向于接受大多数人的观点,而不是对特定风格有充分的理由。

重要的一点是评估为什么使用特定样式以及为什么它比另一种样式更受欢迎 - 换句话说,选择一种样式不仅仅是因为每个人都说这是要做的事情 - 自己思考。

不使用旧式匈牙利语的基本原因是使用的缩写对于每个团队都不同并且难以学习 - 这很容易通过不使用缩写来解决。

随着可用开发工具的变化,样式应该更改为最有意义的样式 - 但每个样式项都有充分的理由。

以下是我的风格指南以及我的理由——我一直在寻找改进我的风格的方法,以创建更可靠、更易于维护的代码。

变量命名约定

我们对变量命名约定都有自己的看法。有许多不同的样式将有助于生成易于维护的高质量代码 - 任何支持有关变量的基本基本信息的样式都可以。特定命名约定的标准应该是它有助于生成可靠且易于维护的代码。不应该使用的标准是: 丑陋的微软(即布拉德艾布拉姆斯)说不要使用那种风格 - 微软并不总是产生最可靠的代码,只是看看 Expression Blend 中的错误。在阅读代码时,变量名称应立即传达有关该变量的三个基本事实,这一点非常重要:它的作用域它的类型清楚地了解它用于作用域:Microsoft 建议完全依赖 IntelliSense。IntelliSense 很棒;但是,人们根本不会将鼠标悬停在每个变量上以查看其范围和类型。假设一个变量在它不在的范围内可能会导致重大错误。例如,如果引用变量作为参数传入并且在本地范围内进行了更改,则该更改将在方法返回后保留,这可能是不需要的。如果在本地范围内修改了字段或静态变量,但有人认为它是局部变量,则可能会导致意外行为。因此,能够只查看一个变量(而不是鼠标悬停)并立即知道它的范围是非常重要的。如果引用变量作为参数传入并在本地范围内更改,则更改将在方法返回后保留,这可能是不需要的。如果在本地范围内修改了字段或静态变量,但有人认为它是局部变量,则可能会导致意外行为。因此,能够只查看一个变量(而不是鼠标悬停)并立即知道它的范围是非常重要的。如果引用变量作为参数传入并在本地范围内更改,则更改将在方法返回后保留,这可能是不需要的。如果在本地范围内修改了字段或静态变量,但有人认为它是局部变量,则可能会导致意外行为。因此,能够只查看一个变量(而不是鼠标悬停)并立即知道它的范围是非常重要的。

建议使用以下表示范围的样式;但是,任何样式都是完全可以的,只要它清楚一致地指示变量的范围: m_ 字段变量 p_ 传递给方法的参数 s_ 静态变量局部变量 类型:如果认为他们正在使用特定类型,则可能会发生严重错误他们实际上是在使用不同的类型——同样,我们根本不会将鼠标悬停在任何变量上来确定它的类型,我们只是假设我们知道它的类型是什么,这就是错误是如何产生的。

缩写:缩写是邪恶的,因为它们对不同的开发人员可能意味着不同的东西。一位开发人员可能认为前导小写“s”表示字符串,而另一位开发人员可能认为它表示有符号整数。缩写是懒惰编码的标志 - 花一点额外的时间并输入全名,以使必须维护代码的开发人员清楚。例如,“str”和“string”之间只有三个字符的区别——让代码易于维护并不需要更多的努力。

内置数据类型的通用且清晰的缩写仅是可接受的,但必须在团队内部进行标准化。

自记录代码:为变量名称添加清晰的描述可以让其他开发人员轻松阅读和理解代码 - 使名称易于理解,以便团队经理无需成为开发人员即可阅读和理解代码。

变量名称部分的顺序:推荐的顺序是作用域类型描述,因为:IntelliSense 将对所有相似作用域进行分组,并且在每个作用域内 IntelliSense 将对所有相似类型进行分组,这使得查找变得容易 - 尝试以其他方式查找变量这使得它非常容易查看和理解范围以及查看和理解类型 这是一种相当常见的样式,易于理解 它将通过 FxCop

示例: 下面是几个示例: m_stringCustomerName p_stringCustomerDatabaseConnectionString intNumberOfCustomerRecords 或 iNumberOfCustomerRecords 或 integerNumberOfCustomerRecords 这些简单的规则将显着提高代码的可靠性和可维护性。

控制结构单行语句 所有控制结构(if、while、for 等)单行语句都应该用大括号括起来,因为很容易添加新语句而不会意识到给定语句属于控制结构,该控制结构将在不产生任何编译时错误的情况下破坏代码逻辑。

方法异常包装 所有方法都应该用一个外部 try-catch 包装,它可以捕获、提供一个地方来恢复、识别、定位、记录,并做出是否抛出的决定。导致我们的应用程序崩溃的是意外异常 - 通过包装捕获所有未处理异常的每个方法,我们保证识别和记录所有异常,并防止我们的应用程序崩溃。这需要更多的工作,但结果非常值得付出努力。

压痕 压痕不是主要问题;但是,建议使用四个空格而不使用制表符。如果打印代码,第一个打印机选项卡通常默认为 8 个空格。不同的开发人员倾向于使用不同的标签大小。Microsoft 的代码通常缩进 4 个空格,因此如果使用任何 Microsoft 代码并且使用 4 个空格以外的空格,则需要重新格式化代码。四个空格使它变得简单和一致。

于 2009-03-18T19:39:18.967 回答
3

除非我坚持使用 vi 或 Emacs 来编辑代码,否则我的 IDE 会为我处理成员的差异显示,因此我很少使用任何特殊约定。这也适用于使用 I 为接口添加前缀或使用 C 为类添加前缀。

请有人解释接口上 I 前缀的 .NET 样式。:)

于 2009-03-18T20:17:25.333 回答
2

每当我有选择的时候,我从不使用任何匈牙利疣。这是额外的打字,并没有传达任何有意义的信息。任何好的 IDE(我根据这个特性的存在来定义“好”,等等)将允许您对静态成员、实例成员、成员函数、类型等有不同的语法突出显示。没有理由让您的包含可由 IDE 提供的信息的代码。这是不将注释掉的旧代码弄乱代码的必然结果,因为您的版本控制系统应该对这些内容负责。

于 2009-03-18T18:49:12.923 回答
2

最好的方法是与你的同事就一个标准达成一致,并坚持下去。它不一定是对每个人都最有效的方法,只是同意一种方法比你实际同意的方法更重要。

我们为代码标准选择的是使用 _ 作为成员变量的前缀。原因之一是它可以很容易地在智能感知中找到局部变量。

在我们就该标准达成一致之前,我使用了另一个标准。我根本没有使用任何前缀,而是写this.memberVariable在代码中以表明我使用的是成员变量。

使用 C# 3 中的属性简写,我发现我使用了很多不那么显式的成员变量。

于 2009-03-18T18:59:17.220 回答
2

最接近官方指南的是StyleCop,这是一种来自 Microsoft 的工具,它可以自动分析您的源文件并检测与推荐的编码风格的违规行为,并且可以在 Visual Studio 和/或 MSBuild 等自动构建中运行。

我们在我们的项目中使用它,它确实有助于使开发人员之间的代码样式和布局更加一致,尽管要注意它确实需要相当多的时间来适应!

回答你的问题 - 它不允许任何匈牙利符号,也不允许任何前缀m_(事实上,它根本不允许使用下划线)。

于 2009-03-18T20:33:07.760 回答
2

我不再使用那种风格了。它旨在帮助您快速了解变量的使用方式。较新的开发环境可让您通过将鼠标悬停在变量上来查看该信息。如果您使用那些较新的工具,对它的需求就会消失。

于 2009-03-18T22:18:41.377 回答
2

还可能从C++ 编码标准Sutter、HerbAlexandrescum Andrei,2004 年)中获得一些见解。项目#0 的标题是“不要为小事出汗。(或:知道什么不应该标准化。)”。

他们通过说“如果您无法决定自己的命名约定,请尝试...私有成员变量likeThis_ ...”,稍微触及了这个特定问题(请记住,前导下划线的使用受 C++ 中非常具体的规则的约束)。

然而,在到达那里之前,他们强调一定程度的一致性“......重要的不是设置规则,而是要与文件中已经使用的样式保持一致......”

于 2009-03-19T14:12:41.270 回答
2

在 C/C++ 中这种表示法的好处是可以更轻松地查看符号的类型,而无需搜索声明。这些样式出现在 Intellisense 和“Go to Definition”出现之前——我们经常不得不在不知道有多少头文件中寻找声明。在一个大型项目中,这可能是一个很大的烦恼,在查看 C 源代码时已经够糟糕了,但在使用混合的汇编+源代码和原始调用堆栈进行取证时更糟糕。

当面对这些现实时,即使有维护开销,使用 m_ 和所有其他匈牙利规则也开始变得有意义,因为在查看不熟悉的代码时只需查找符号类型就可以节省多少时间。现在我们当然有了 Intellisense 和“Go to Definition”,因此该命名约定的主要节省时间的动机不再存在。我认为这样做没有多大意义,我通常会尝试遵循 .NET 库指南,只是为了保持一致并可能获得更多的工具支持。

于 2009-03-20T14:44:47.497 回答
1

如果您没有根据特定准则进行编码,则应继续使用您的实际m_符号,并在项目编码准则要求时更改它。

于 2009-03-18T19:34:09.807 回答
0

发挥作用。

  • 不要使用全局变量。
  • 不要使用静态变量。
  • 不要使用成员变量。

如果你真的需要,但只有你真的需要,使用一个且只有一个变量来访问你的应用程序/环境。

于 2009-03-18T23:02:40.427 回答