命名约定-C ++和C#变量中的下划线


146

_var在类字段中看到变量名是很常见的。下划线是什么意思?所有这些特殊的命名约定都有参考吗?



17
一些误导的公司编码指南建议在成员变量中添加疣,以将其与局部变量区分开,这是因为人们认为类和函数将不可避免地变得that肿,以至于没有这样模糊的线索就无法跟踪问题所在。提出此类约定的人通常都是代码最需要它们的人。
Mike Seymour 2010年

26
@Mike-您的一揽子声明是不正确的。我发现使用此约定使快速扫描方法和很好地理解该类变得容易得多。
ChaosPandion 2010年

12
@ChaosPandion:然后不要将其作为笼统的声明来阅读。“有些”并不意味着“全部”,“太多”并不意味着“总是”,也许您是我的经验的例外。
Mike Seymour 2010年

17
在C ++中,避免导致下划线。在许多情况下(例如,在全球范围内,大写字母后面等),它们是为实现保留的,实际上您可能会遭受一些宏践踏。因此,如果需要它们,请使它们在下划线后面
2010年

Answers:


120

下划线只是一个约定;而已。因此,每个人的使用总是有些不同。以下是我对两种语言的理解:

在C ++中,下划线通常表示私有成员变量。

在C#中,我通常看到它仅在定义公共属性的基础私有成员变量时使用。其他私有成员变量将没有下划线。但是,随着自动属性的出现,这种用法在很大程度上已被淘汰。

之前:

private string _name;
public string Name
{
    get { return this._name; }
    set { this._name = value; }
}

后:

public string Name { get; set; }

11
希望其属性不变的团队除外。
ChaosPandion 2010年

11
下划线开头的标识符保留给编译器使用。使用前缀下划线可能会与编译器符号冲突。
Thomas Matthews 2010年

12
@Thomas Matthews-不在C#中。
EMP 2010年

11
@ChaosPandion我假设您所说的“不可变”实际上是“只读”,在这种情况下,可以使用public string Name { get; private set; }。是的,它不是完全不变的,但它在那里。
jdmichal 2010年

7
@Thomas在类上下文中,标识符以下划线开头,后跟一个大写字母,并在C ++中保留。_var不保留。
泰勒·麦克亨利

84

最佳实践是不要在C ++中的任何变量名称或参数名称之前使用UNDERSCORES

C ++实现者保留以下划线或双下划线开头的名称。带下划线的名称保留给库使用。

如果您阅读了C ++编码标准,您会在第一页看到它的内容:

“不要过分命名,但要使用一致的命名约定:必须做的只有两个:a)切勿使用“下划线名称”,即以下划线开头或包含双下划线的名称;” (p2,C ++编码标准,Herb Sutter和Andrei Alexandrescu)

更具体地说,ISO工作草案规定了实际规则:

另外,某些标识符保留供C ++实现使用,否则不得使用。无需诊断。(a)包含双下划线__或以下划线后跟大写字母开头的每个标识符都保留给实现以供任何使用。(b)每个以下划线开头的标识符都保留给实现,以用作全局命名空间中的名称。

最好避免在下划线开头的符号,以防您意外进入上述限制之一。

您可以亲眼看到为什么在开发软件时使用下划线会造成灾难性的后果:

尝试像这样编译一个简单的helloWorld.cpp程序:

g++ -E helloWorld.cpp

您将在后台看到所有发生的情况。这是一个片段:

   ios_base::iostate __err = ios_base::iostate(ios_base::goodbit);
   try
     {
       __streambuf_type* __sb = this->rdbuf();
       if (__sb)
  {
    if (__sb->pubsync() == -1)
      __err |= ios_base::badbit;
    else
      __ret = 0;
  }

您会看到多少个名字以双下划线开头!

同样,如果您查看虚拟成员函数,您会看到* _vptr是为虚拟表生成的指针,当您在类中使用一个或多个虚拟成员函数时,该表会自动创建!但这是另一个故事...

如果使用下划线,则可能会遇到冲突问题,并且根本不会导致问题的发生,直到为时已晚。


4
不是全局或文件作用域的名称不是以下划线和小写字母开头的吗?我一直这样做,因为在某些情况下它非常干净,例如:void A :: set_size(int _size){size = _size; }。您如何为这样的琐碎用途做些什么?
tukra

3
我认为在非全局/文件范围内允许在下划线后加上小写字母。您能告诉我标准中什么地方没有吗?下划线的问题是我有时已经将其用于成员var。所以我可能有函数'int size();' 成员var'int size_;' 和参数'int _size'。这往往可以很好地涵盖所有内容,而且我还没有看到更好的选择。与STL一起使用的泛型不能使用大写的功能。C#人们喜欢的“ this.size”样式似乎很容易出错,除非您始终将其用于成员访问,这很难看。
tukra

7
我了解有些人不希望使用下划线和小写字母,因为他们可能难以弄清什么是合法的。即使有些作者建议不要使用C ++标准,也可以使用它。对我来说,由于它是完全合法的并且没有保留,所以我认为没有任何理由认为它有风险。感谢您的反馈意见。
tukra

3
@tukra:这不是完全合法的。C ++标准允许您在本地范围内使用单个下划线,后跟小写字母。祝好运!:-)
Constantinos Glynos

4
抱歉。我认为我们过去需要重复资格考试。除文件作用域外,在所有作用域中都允许使用以下划线开头,后跟小写字母的C ++标识符。它在函数,函数原型,类,结构,联合,枚举以及放置在名称空间中的所有内容上都是安全合法的。如果遵循将所有代码放入命名空间的通用做法,那么您将完全安全。
tukra

45

实际上,_var约定来自VB,而不是C#或C ++(m _,...是另一回事)。

这就克服了在声明属性时VB不区分大小写的问题。

例如,这样的代码在VB中是不可能的,因为它会将userUser视为相同的标识符

Private user As String

Public Property User As String
  Get
    Return user
  End Get
  Set(ByVal Value As String)
    user = value
  End Set
End Property

因此,为了克服这一点,有人使用约定将“ _”添加到私有字段中,如下所示

Private _user As String

Public Property User As String
  Get
    Return _user
  End Get
  Set(ByVal Value As String)
    _user = value
  End Set
End Property

由于.Net有许多约定,并且为了在C#和VB.NET约定之间保持一定的一致性,因此它们使用的是同一约定。

我找到了我所说的参考:http : //10rem.net/articles/net-naming-conventions-and-programming-standards---best-practices

驼色表壳带下划线 在VB.NET中,始终指示“受保护”或“私有”,请勿使用“昏暗”。不鼓励使用“ m_”,也不鼓励使用仅与大小写不同的变量名,特别是对于受保护的变量,因为它违反了法规遵从性,如果您在VB.NET中进行编程,将会使您的生活痛苦必须为您的成员命名不同于accessor / mutator属性的名称。在这里所有项目中,领先的下划线确实是唯一有争议的项目。对于我的私有变量,我个人更喜欢使用不带下划线的驼峰样式,这样我就不必用“ this”来限定变量名。区别于构造函数或其他可能发生命名冲突的地方的参数。使用VB.NET不区分大小写,这尤其重要,因为除下划线外,访问器属性通常与私有成员变量具有相同的名称。就m_而言,它实际上只是关于美学。我(和许多其他人)发现m_丑陋,因为看起来变量名中有一个洞。这几乎是令人反感的。我曾经在VB6中一直使用它,但这仅仅是因为变量不能有前导下划线。我很高兴看到它消失了。Microsoft建议不要使用m_(和直线_),即使它们在代码中都使用了。同样,以直线“ m”作为前缀也是正确的。当然,由于它们主要使用C#进行编码,因此它们可以具有仅在属性上有所区别的私有成员。VB人员必须做其他事情。我建议不要尝试提出每种语言的特殊情况,而建议使用所有支持该语言的下划线。如果我希望我的类完全兼容CLS,则可以在所有受C#保护的成员变量上都省略前缀。但是,实际上,我永远不必担心这一点,因为我将所有可能受保护的成员变量保持私有状态,而是提供受保护的访问器和变量。原因:简而言之,此约定很简单(一个字符),易于阅读(您的眼睛不会被其他前导字符分散注意力),并且成功避免了与过程级变量和类级属性的命名冲突。 。对于所有支持它的语言,我都建议使用下划线。如果我希望我的类完全兼容CLS,则可以在所有受C#保护的成员变量上都省略前缀。但是,实际上,我永远不必担心这一点,因为我将所有可能受保护的成员变量保持私有状态,而是提供受保护的访问器和变量。原因:简而言之,此约定很简单(一个字符),易于阅读(您的眼睛不会被其他前导字符分散注意力),并且成功避免了与过程级变量和类级属性的命名冲突。 。对于所有支持它的语言,我都建议使用下划线。如果我希望我的类完全兼容CLS,则可以在所有受C#保护的成员变量上都省略前缀。但是,实际上,我永远不必担心这一点,因为我将所有可能受保护的成员变量保持私有状态,而是提供受保护的访问器和变量。原因:简而言之,此约定很简单(一个字符),易于阅读(您的眼睛不会被其他前导字符分散注意力),并且成功避免了与过程级变量和类级属性的命名冲突。 。我从不担心此问题,因为我将所有可能受保护的成员变量设为私有,而是提供受保护的访问器和变量。原因:简而言之,此约定很简单(一个字符),易于阅读(您的眼睛不会被其他前导字符分散注意力),并且成功避免了与过程级变量和类级属性的命名冲突。 。我从不担心此问题,因为我将所有可能受保护的成员变量设为私有,而是提供受保护的访问器和变量。原因:简而言之,此约定很简单(一个字符),易于阅读(您的眼睛不会被其他前导字符分散注意力),并且成功避免了与过程级变量和类级属性的命名冲突。 。


6

第一个评论者(R Samuel Klatchko)提到:在C ++标识符中使用下划线的规则是什么?它回答了有关C ++下划线的问题。通常,您不应该使用前划线,因为它是为编译器的实现者保留的。您看到的代码_var可能是遗留代码,或者是使用旧命名系统长大的人编写的代码,而该命名系统并没有引起人们的注意。

正如其他答案所指出的那样,它曾经在C ++中用于标识类成员变量。但是,就装饰器或语法而言,它没有特殊含义。因此,如果要使用它,它将进行编译。

我将把C#讨论留给其他人。


5

_var没有意义,仅用于简化变量是私有成员变量的目的。

在C ++中,使用_var约定是错误的形式,因为在标识符前面有一些规则来控制下划线的使用。_var保留为全局标识符,而_Var(下划线+大写字母)随时保留。这就是为什么在C ++中,您会看到人们使用var_约定的原因。



2

对于C#微软的命名标准说变量和参数应使用低驼峰形式IE: paramName。该标准还要求字段遵循相同的格式,但这可能导致代码不清晰,因此许多团队要求使用下划线前缀来提高IE的 清晰度_fieldName


2

对于C#,Microsoft Framework设计指南建议不要对公共成员使用下划线字符。对于私人成员,可以使用下划线。实际上,Jeffrey Richter(在指南中经常被引用)例如使用m_来表示私有静态成员,并使用“ s_”。

就个人而言,我仅使用_标记我的私人成员。“ m_”和“ s_”紧靠匈牙利表示法,不仅在.NET中被皱眉,而且可能很冗长,我发现很多成员的类很难按字母顺序进行快速眼动扫描(想象10个变量都以m_开头) 。


由于匈牙利符号表示类型,所以我认为您真的不能说M / S接近于它。
杰里·尼克松

@ JerryNixon-MSFT在涉及匈牙利概念时,我们是否将变量数据类型和变量可见性类型识别为同一事物?
Necro

1

我将_var命名用于类的成员变量。我这样做有两个主要原因:

1)当我稍后阅读代码时,它可以帮助我跟踪类变量和局部函数变量。

2)当我在寻找一个类变量时,它对Intellisense(或其他代码完成系统)有帮助。仅知道第一个字符有助于筛选可用变量和方法的列表。


1
依靠悬停智能感知或扫描以查看var是否在本地定义,这是较大方法的麻烦。出于与您提到的相同的原因,也倾向于使用“ __”作为静态变量,但目前不受欢迎。
crokusek

1

就C和C ++语言而言,名称中的下划线(开头,中间或结尾)没有特殊含义。这只是一个有效的变量名字符。“惯例”来自编码社区内的编码实践。

如上面的各种示例所示,_开头可能表示C ++中某个类的私有或受保护成员。

让我仅提供一些可能有趣的琐事的历史。在UNIX中,如果您具有核心C库函数和内核后端,并且您想在其中将内核函数公开给用户空间,则_会停留在直接调用内核函数而无需执行其他任何操作的函数存根前面。最著名和熟悉的示例是BSD和SysV类型内核下的exit()与_exit():在那里,exit()在调用内核的退出服务之前执行用户空间操作,而_exit仅映射到内核的退出服务。

因此,_用于“本地”内容,在这种情况下,本地是机器本地的。通常,_functions()不可移植。这样一来,您不应期望跨各种平台具有相同的行为。

现在关于变量名中的_,例如

int _foo;

从心理上讲,必须在开始时输入_是很奇怪的事情。因此,如果要创建一个变量名,使其与其他事物发生冲突的可能性较小,尤其是在处理预处理程序替换时,请考虑使用_。

我的基本建议是始终遵循编码社区的惯例,以便您可以更有效地进行协作。


0

这只是意味着它是类中的成员字段。



0

许多人喜欢在私有字段前面加上下划线。这只是一个命名约定。

C#的“官方”命名约定为私有字段规定了简单的小写名称(无下划线)。

尽管下划线已被广泛使用,但我不了解C ++的标准约定。


0

这只是一些约定,某些程序员在操作类的成员或其他类型的变量(参数,函数局部变量等)时要弄清楚。成员变量也广泛使用的另一种约定是在名称前加上'm_'。

无论如何,这些只是约定,您将找不到所有这些的单一来源。它们只是风格问题,每个编程团队,项目或公司都有自己的(甚至根本没有)。


0

有充分合法的理由在C#中使用它:如果代码也必须可以从VB.NET扩展。(否则,我不会。)

由于VB.NET不区分大小写,因此没有简单的方法来访问field此代码中的受保护成员:

public class CSharpClass
{
    protected int field;
    public int Field { get { return field; } }
}

例如,这将访问属性获取器,而不是字段:

Public Class VBClass
    Inherits CSharpClass

    Function Test() As Integer
        Return Field
    End Function

End Class

哎呀,我什至不能写field小写-VS 2010一直在纠正它。

为了使它易于访问VB.NET中的派生类,必须提出另一种命名约定。给下划线加上前缀可能是它们中插入最少且最“历史上接受的”。


0

现在,对于C#类成员变量,在this.foobarbaz中使用“ this”表示法是可以接受的。它替代了旧的“ m_”或“ __”符号。它的确使代码更具可读性,因为毫无疑问正在引用什么。


0

根据我的经验(一定是有限的),下划线将指示它是一个私有成员变量。正如Gollum所说,这将取决于团队。


0

旧问题,新答案(C#)。

C#下划线的另一种用法是使用ASP NET Core的DI(依赖注入)。readonly在构造期间分配给注入接口的类的私有变量应以下划线开头。我想是否对课程的每个私人成员都使用下划线是一个辩论(尽管Microsoft本身也遵循),但这是肯定的。

private readonly ILogger<MyDependency> _logger;

public MyDependency(ILogger<MyDependency> logger)
{
    _logger = logger;
}

-2

这样的命名约定在您阅读代码(尤其是非您自己的代码)时非常有用。强大的命名约定有助于指示特定成员的定义位置,成员的种类等。大多数开发团队采用简单的命名约定,并简单地用下划线(_fieldName)前缀成员字段。过去,我为C#使用以下命名约定(基于Microsoft的.NET Framework代码约定,可以在Reflector上看到):

实例字段: m_fieldName
静态字段: s_fieldName
公共/受保护/内部成员: PascalCasedName()
私有成员: camelCasedName()

这有助于人们在快速阅读不熟悉的代码时了解成员的结构,使用,可访问性和位置。


3
实际上,Microsoft不建议使用m_,s_前缀。不要在字段名称中使用前缀。例如,请勿使用g_或s_区分静态字段和非静态字段。 msdn.microsoft.com/zh-CN/library/ms229012.aspx
Zied 2010年

1
就像我说的,用Reflector看看他们的源代码。他们会推高自己的建议,您会感到惊讶。;)正如我的文章所述,它有助于提高代码的可读性,而我的上一个团队真的很喜欢上面列出的命名约定。
jrista 2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.