注释代码的样式和建议


26

我想听听您在代码中编写注释的任何建议和经验。您如何以最简单,最有用的方式编写它们?注释部分代码时有什么习惯?也许一些异国情调的建议?

我希望这个问题能收集到最有趣的建议和建议,以供大家参考。

好,我开始。

  1. 通常,/* */即使我需要注释很多行,我也不使用注释。

    优点:与在单行注释中混合使用这种语法相比,代码在视觉上看起来更好。大多数IDE都具有注释选定文本的功能,并且通常使用单行语法来注释。

    缺点:没有IDE,很难编辑这样的代码。

  2. 在所有已完成评论的末尾放置“点”。

    例如:

    //Recognize wallpaper style. Here I wanted to add additional details
    int style = int.Parse(styleValue);
    //Apply style to image.
    Apply(style);
    

    优点:仅在您完成的注释中放置“点”。有时您可以写时间信息,因此缺少“点”将告诉您您要返回并在此注释中添加一些其他文本。

  3. 对齐枚举中的文本,注释参数等。

    例如:

    public enum WallpaperStyle
    {
        Fill = 100,     //WallpaperStyle = "10"; TileWallpaper = "0".
        SizeToFit = 60, //WallpaperStyle = "6";  TileWallpaper = "0".
        Stretch = 20,   //WallpaperStyle = "2";  TileWallpaper = "0".
        Tile = 1,       //WallpaperStyle = "0";  TileWallpaper = "1".
        Center = 0      //WallpaperStyle = "0";  TileWallpaper = "0".
    };
    

    优点:看起来更好,视觉上更容易找到所需的东西。

    缺点:花时间对齐并且难以编辑。

  4. 用注释编写文本,这是通过分析代码无法获得的。

    例如,愚蠢的评论:

    //Apply style.
    Apply(style);
    

    优点:您将获得清晰,小巧的代码,并且注释中仅包含有用的信息。


2
在vim中对齐注释:使用Align.vim并:3,7 Align //在3-7行对齐。
贝努瓦

3
“很难不编辑IDE” -好了,你做的是经常?

3
我认为在问题中应该注意语言/环境偏好。其中一些具有现有准则(.NET具有相当标准的xml注释:msdn.microsoft.com/en-us/library/b2s063f7.aspx)。
史蒂文·埃弗斯

+1 SnOrfus。对于要用于Javadocs的Java注释,需要将开发人员文档放在双星号注释中,该注释必须放在代码之前。而且Javadoc注释已转换为html,因此您可以在注释中使用项目符号列表,表格,图像或url,在所有情况下,尾随的点都可能会令人不安。
用户未知,

Answers:


16

下面的一些陈述是非常个人化的,尽管有一定道理,并且是这种方式。

评论类型

对于简短版本...我使用以下注释:

  • 解释数据结构中字段的尾随注释(除那些之外,我并不真正使用单行注释)
  • 块上方的特殊或针对性的多行注释
  • 从源生成的公共用户和/或开发人员文档

请阅读下面的详细信息和(可能不清楚的)原因。

尾随评论

根据语言,使用单行注释还是多行注释。为什么要依赖?这只是一个标准化问题。在编写C代码时,默认情况下,我偏爱老式的ANSI C89代码,因此我更喜欢始终使用/* comments */

因此,在大多数情况下,我都会在C语言中使用它,有时(取决于代码库的样式)对于使用类似C语言的语法的语言而言:

typedef struct STRUCT_NAME {
  int fieldA;                /* aligned trailing comment */
  int fieldBWithLongerName;  /* aligned trailing comment */
} TYPE_NAME;

Emacs很不错,可以帮我做到M-;

如果该语言支持单行注释而不是基于C的语言,那么我会更热衷于使用单行注释。否则,恐怕我已经养成了习惯。这并不一定很糟糕,因为它迫使我变得简洁。

多行注释

我不同意您使用单行注释的说法,因为这在视觉上更具吸引力。我用这个:

/*
 * this is a multi-line comment, which needs to be used
 * for explanations, and preferably be OUTSIDE the a
 * function's or class' and provide information to developers
 * that would not belong to a generated API documentation.
 */

或这个(但我不再那么频繁了,除了在个人代码库上或者主要是为了获得版权声明-这对我来说是历史性的,来自我的教育背景。不幸的是,大多数IDE在使用自动格式化时会搞砸了)

/*
** this is another multi-line comment, which needs to be used
** for explanations, and preferably be OUTSIDE the a
** function's or class' and provide information to developers
** that would not belong to a generated API documentation.
*/

如果确实需要,那么如果可以在尾随位置使用它,则可以使用前面提到的尾随注释来内联注释。例如,在非常特殊的返回情况下,或者记录switchcase语句(很少见,我不经常使用switch),或者在if ... else控制流中记录分支时。如果这不是其中之一,通常对作用域之外的注释块概述函数/方法/块的步骤对我来说更有意义。

我非常例外地使用它们,除非使用不支持文档注释的语言进行编码(请参见下文);在这种情况下,它们变得更加普遍。但是在一般情况下,它实际上仅是为了记录那些对其他开发人员有意义的事情,并且是真正需要脱颖而出的内部注释。例如,要记录一个强制性的空块(如“强制” catch块):

try {
  /* you'd have real code here, not this comment */
} catch (AwaitedException e) {
  /*
   * Nothing to do here. We default to a previously set value.
   */
}

这对我来说已经很丑陋,但在某些情况下我会容忍。

文档注释

Javadoc等

我通常在方法和类上使用它们,以记录引入功能(或更改功能)的版本(尤其是在公共API专用的情况下),并提供一些示例(明确的输入和输出用例以及特殊用例)。尽管在某些情况下用单位案例来记录它们可能更好,但是单位测试不一定是人类可读的(无论您使用什么DSL东西)。

他们使我有点烦恼文档字段/属性,因为我更喜欢尾随注释,而并非所有文档生成框架都支持尾随文档注释。例如,Doxygen可以,但是JavaDoc则不可以,这意味着您需要为所有字段添加最上面的注释。不过,我可以幸免于难,因为在大多数情况下,Java行总是相对较长,因此通过将行扩展到超出我的容忍阈值,尾随注释将使我同样地感到毛骨悚然。如果Javadoc会考虑改善这一点,我会很高兴。

注释掉的代码

我仅出于一种原因使用单行代码(类似于C语言)(除非针对严格的C进行编译,否则我实际上不使用它们):在编码时将内容注释掉。大多数IDE都具有单行注释切换功能(按缩进或第0列对齐),并且适合我的用例。使用多行注释切换(对于某些IDE,在行中间进行选择)将使在注释/取消注释之间轻松切换变得更加困难。

但是由于我反对SCM中的注释掉的代码,因此通常寿命很短,因为我将在提交之前删除注释掉的块。(请阅读“在线注释和SCM编辑”中该问题的回答

评论样式

我通常倾向于写:

  • 用于文档注释的具有正确语法(包括标点符号)的完整句子,因为它们应该稍后在API文档中阅读,甚至可以作为生成的手册的一部分阅读。
  • 格式正确,但对多行注释块的标点/大写比较宽松
  • 尾随的块而没有标点符号(由于篇幅所致,通常是因为注释是简短的注释,其读起来更像是带括号的语句)

关于文字编程的注意事项

正如Donald Knuth本文中介绍的那样,您可能想对Literate Programming感兴趣。

熟练的编程范例代表了一种从计算机强加的方式和顺序编写程序的转变,而使程序员能够按照其逻辑和思想流所要求的顺序来开发程序。2精巧的程序是用普通的人类语言作为对逻辑的不间断的阐述而编写的,就像论文的文本一样。

精巧的编程工具用于从有文化的源文件中获得两种表示形式:一种适用于计算机的进一步编译或执行,“纠结”的代码,另一种适用于查看格式化的文档,据称是从文档中“编织”来的。识字的来源。

作为旁注和示例:尽管未遵守我的评论风格,underscore.js JavaScript框架还是一个文档良好的代码库和格式正确的带注释的源代码的很好的示例-尽管可能不是最好的用法API参考)。


这些是个人惯例。是的,我可能很奇怪(您也可能如此)。没关系,只要您在与同龄人一起工作时遵循并遵守团队的代码约定,或者不从根本上攻击他们的喜好并与他们同居。这是您风格的一部分,您应该找到一条明确的界线,即发展一种将您定义为编码员(或与您有联系的思想流派或组织的追随者)的编码风格,遵守团体的一致性约定。


+1用于区分注释掉的代码和文档注释。我讨厌追捕那些人:P
deltreme

@deltreme:谢谢。我感到您的痛苦,我也在我当前的产品中寻找很多这样的人。SCM的存在是有原因的......我很诱惑,只是用一个正则表达式全文搜索在Eclipse或Emacs,只是根除逐一......我有更多富有成效的事情要做,可悲的是:(
haylem 2011年


14

当我上大学时,我总是被教导要注释每一行代码和每个方法标头。它被打鼓/灌输到一定程度,以至于您毫无疑问地做到了。我曾在不同公司的几个敏捷开发团队中工作过,我可以说我可能会在一个蓝色的月亮上发表评论。

原因有两点,首先,我们不应该再写长的整体方法来处理101种不同的事情,而类,方法和变量名应该是自我记录的。以以下登录方式为例。

public void Login(string username, string password)
{
    // Get the user entity
    var user = userRepository.GetUser(username);


    // Check that the user exists
    if (user == null)
    {
        throw new UserNotFoundException();
    }

    // Check that the users password matched
    if (user.HashedPassword != GetPasswordHash(password))
    {
        throw new InvalidUsernamePasswordException();
    }

    //Check that the users account has not expired
    if (user.Expired)
    {
        throw new UserExpiredException();
    }

    //Mark user as logged in
    ...
}

可以将其改写成更具可读性和重用性的东西:

public void Login(string username, string password)
{
    var user = GetUserForUsername(username);

    CheckUsersPasswordMatched(user, password);

    CheckUserAccountNotExpired(user);

    MarkUserAsLoggedIn(user);
}

private void User GetUserForUsername(string username)
{
    var user = userRepository.GetUser(username);

    if (user == null)
    {
        throw new UserNotFoundException();
    }
    return user;
}

private void CheckUsersPasswordMatched(User user, string password)
{
    if (user.HashedPassword != GetPasswordHash(password))
    {
        throw new InvalidUsernamePasswordException();
    }
}

private void CheckUserAccountNotExpired(User user)
{
    if (user.Expired)
    {
        throw new UserExpiredException();
    }
}

您可以从登录方法中清楚地看到发生了什么。您可能会认为这是额外的工作,但是您的方法很小,只有一份工作。另外,方法名称是描述性的,因此无需编写任何方法标题注释。如果最终使用太多方法,则表明相关方法应重构为另一个对象,例如UserAuthenticationService,请记住,一个对象应仅具有一项工作。

其次,您编写的每一段代码(包括注释)都必须维护,注释越多,维护的内容就越多。如果重命名类或变量,则会出现编译错误,但是如果您更改代码段的工作方式或将其删除,并且不更新任何相关注释,则不会出现编译错误,并且注释会四处徘徊,引起混乱。

如果您正在编写API,那么我相信任何面向公众的接口,类,枚举都应具有写得很好的标头注释以用于文档编制。


2
我完全同意这种说法。众所周知的简短方法是自我记录。我经常在代码中写很少(如果有的话)注释,并且我会写一个带有代码示例的相当大的Wiki页面(通常在编写其他开发人员将使用的库时完成)。
凯文

2
这正是我来这里说的。实际上,与编写代码一样,我花了很多时间在思考变量名,方法名,类名等。我相信结果是非常可支持的代码。当然,有时我会使用诸如checkIfUnitInvestigationExistsAndCreateNewRootCauseListIfItDoes()之类的方法命名。是的,方法名称有时会很长,但是我认为代码的可支持性是开发中最重要的方面(除了发布速度之外)。
jeremy.mooer 2011年

5

较少关注格式,而更多关注内容。例如,您示例中的评论告诉我没有新内容。它们比没有价值要糟,因为它们会影响阅读代码,并且诸如此类的注释充其量只能模糊地提及原始程序员在编写代码时的想法。从代码示例中可以看到您正在应用样式“ apply(Style)”,我可以阅读源代码。我看不懂你的想法,这是评论为什么要告诉我你为什么这样做。例如而不是

//Apply style.

Apply(style);

应该

// Unlike the others, this image needs to be drawn in the user-requested style apply(style);

我们大多数人使用现有代码在团队中工作,格式化团队其他成员的工作方式以及已经完成的工作方式。一致性远比漂亮重要。


仔细阅读该示例的用途。我已经提到过:“例如,愚蠢的评论:”。
Kyrylo M

1
我明白你的意思。我相信您不会对在实际代码中看到多少“愚蠢的评论”感到惊讶,因此我支持我的文章。格式无所谓,内容无所谓。
mattnz

3

尽可能编写代码,使注释完全多余。仅当无法以使重要概念显而易见的方式编写代码时,才添加注释。


2

我自己的偏好是保持它非常简单。我回避各种花哨的格式。这样做的主要原因是,我认为即使使用最简单的文本编辑器,也可以轻松地编辑源代码。我也从不硬包装文本段落,而是让编辑器进行软包装(不添加换行符)。


我从未在评论中看到软包装。我不认为这是个好主意,但我想只要保持一致就可以。
亚当·伯特

2

我经常看到这样的评论,一些工具会以这种方式自动生成它:

/** 
 * This is an example, how to waste vertical space,
 * and how to use useless asterixes.
 */

少两行:

/** This is an example, how to spare vertical space,
    and how to avoid useless asterixes. */

IDE和编辑器位于记事本级别的上方,能够检测注释并以其他颜色打印它们。无需用星号修饰该行的开头。

如果使用制表符进行缩进,则甚至可以节省一些字节。

如果您不使用复杂的编辑器(以灰色调显示注释),则大量的星号将成为重点并吸引您的注意力,这与正确的做法相反:留在后面。


在这种情况下,如果您要节省屏幕空间,则IDE和编辑器可以使用代码折叠。如果您要保存字节,则需要停止在Commodore 64上进行编码:)更严重的是,如果要保存字节(例如,用于客户端代码),那么编译器或压缩器将为您完成此操作在生产中不需要评论。代码大小很重要,因为您拥有的代码越多,发生错误的可能性就越大(可以说)。但是,在现代过程中,总文件大小并不是真正要考虑的问题。将代码存储在SCM中,并进行相应维护。
2011年

如果使用糟糕的编辑器,星号不会引起我的注意,因为它们是注释,它们的对齐方式很清楚。如果我正在阅读一种动态脚本语言的代码,那么用笨拙的编辑器来使用您的样式而又不会突出显示任何内容,这将使我眼花hard乱,因为这将花费我更长的时间来处理我在说的是整个注释块或一些奇怪包装的代码语句。虽然这可能是人,也是一个人的习惯的结果,但这就是我的看法。
2011年

我不喜欢花时间折叠和展开代码。如果字节有一个优势,我同意您的准将论点,但事实却并非如此。如果您的编辑器没有代码突出显示,那就去买一台Commodore64。:) indentation参数不成立,因为如果缩进将注释与代码分隔开,则也会将代码与注释分隔开。查看更大的一段带注释的代码-突出显示一个星号块。
用户未知,

就像我说的,可能是个人的。但是请考虑一下:浏览网页时,您是否真的看到了所有那些闪亮而时髦的广告?大多数人没有。您只需学会将它们屏蔽掉,因为您已经将它们注册为一般模式,因此可以轻松地进行屏蔽。适用于我的文档注释。关于折叠,这可能是乏味的。对于Java,默认情况下,Eclipse被设置为折叠很多东西,因为我喜欢打开Java文件并能够像C头文件一样查看它们(不使用大纲视图)。而且我使用Mylyn仅显示我正在处理的位。
Haylem 2011年

是的,我学会了使用名为广告拦截器的插件将其屏蔽。Eclipse具有折叠功能,但我用于小型单个文件程序的gedit却没有。
用户未知,

2

这是我在整个工作代码中发现的“反模式”:将注释用作“更改日志”;这就是版本控制系统中的日志。该代码到处都是:

// 05-24-2011 (John Doe): Changed this method to use Foo class instead of Bar

通常包含被注释掉的旧代码(同样,这是VCS系统的要点,因此在编写新代码后无需将其包含在代码中)。同样需要避免的是重复的评论,例如“我们为什么需要这个?” 甚至更糟的是,“可能应该重命名”(因为有用于重命名的复杂工具,因此在花费您编写注释的时间,您可能已经将其重命名了)。同样,我会定期处理以下两个评论:

// (John Doe) 05-24-2011 not sure why we are using this object?
FooBar oFooBar = Quux.GetFooBar(iFooBarID, bSomeBool);
oFooBar.DiscombobulateBaz();

// (John Doe). This method is poorly named, it's used for more 
// than just frazzling arvadents
public int FrazzleArvadent(int iArvadentID)

2
  1. 选择一个文件系统,如 doxygen的,并坚持下去。继续检查产生的文件。
  2. 尝试给新手介绍一个基本的人,并阅读您的文档,他们能从头开始吗?实习生实际上对此有好处,将新员工与您现有的文档库放在一起,并完成一个简单的任务,看看他们能走多远(如果绊倒了),可以肯定,您告诉他们的一切都可以使他们再次进入文档。
  3. 在审核过程中使文档注释成为检查点。

1

代码阅读器通常试图回答三个问题:

  1. 这个类或函数做什么?如果这很难回答,那么它会做得太多。难以记录的代码通常只是错误的。
  2. 如何使用?一个例子可能就足够了。
  3. 该代码令人惊讶。你为什么这么做?最可能的答案:解决第三方组件中的错误,因为事实证明这种明显的技术太慢了

其他所有内容都应在代码中表达。就像撰写散文一样,这是一门艺术,需要大量练习。知道代码是否可以理解的唯一方法是让其他人阅读它。当他们不懂某事时,不要口头解释。改进代码。作为最后的选择,添加评论。

如果看到“两倍长度”,我会问“测量单位是多少?” 不要添加评论。更改变量名称。如果看到代码块并且说“这有什么用?”,请不要添加注释。提取具有有意义名称的函数。如果由于需要17个参数而无法提取函数,请重构代码。

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.