11

复制

你对评论有什么硬性规定?

与我一起工作的开发人员对我感兴趣的评论有一些话要说(见下文)。你个人对评论的态度/看法是什么?

“我不会在代码中添加注释,除非它是一个简单的标题,或者存在
平台错误或不明显的必要解决方法。代码可以更改,注释可能会产生误导。代码
在使用时应该是自我记录的描述性名称及其逻辑
组织 - 及其解决方案应该是
执行给定任务的最干净/最简单的方法。如果程序员不能
仅通过阅读代码来判断程序做什么,那么他还没有准备好改变它
。往往是编写复杂或
不明显的东西的拐杖——我的目标是始终编写干净简单的代码。”

“我认为在评论方面有几个阵营,认为他们正在编写 API 和一些宏大的代码库的企业类型将用于子孙后代,类似工匠的程序员认为代码说明了什么它确实比注释更清晰,新手编写冗长/不清晰的代码以便需要给自己留下笔记,说明他们为什么要做某事。”

4

26 回答 26

30

“自我记录代码”理论存在一个可悲的缺陷。是的,阅读代码会告诉你它做什么。但是,代码无法告诉您它该做什么。

我认为可以肯定地说,所有的错误都是在代码没有做它应该做的事情时引起的:)。因此,如果我们添加一些关键注释来为维护者提供足够的信息来了解一段代码应该做什么,那么我们就赋予了他们修复大量错误的能力。

这给我们留下了要输入多少评论的问题。如果您输入太多评论,维护起来就会变得乏味,并且评论将不可避免地与代码过时。如果你投入的太少,那么它们就不是特别有用。

我发现常规评论在以下地方最有用:

1) .h 或 .cpp 文件顶部的简要说明,用于解释类目的。这有助于为维护人员提供快速概览,而无需筛选所有代码。

2) 在执行一个重要函数之前的注释块,解释它的目的并详细说明它的预期输入、潜在输出以及调用函数时预期的任何奇怪之处。这使未来的维护者不必破译整个函数来弄清楚这些事情。

除此之外,我倾向于评论任何可能让某人感到困惑或奇怪的事情。例如:“这个数组是基于 1 而不是基于 0,因为 blah blah”。

写得好,位置好的评论是无价的。糟糕的评论往往比没有评论更糟糕。对我来说,完全没有任何评论表明代码作者的懒惰和/或傲慢。无论代码在做什么对您来说多么明显,或者您的代码多么出色,进入一个冷酷的代码体并弄清楚到底发生了什么都是一项具有挑战性的任务。做得好的评论可以让人们加快现有代码的速度。

于 2009-02-01T01:25:27.833 回答
17

我一直很喜欢Refactoring对评论的看法:

我们在这里提到评论的原因是评论经常被用作除臭剂。令人惊讶的是,您经常看到注释很厚的代码并注意到注释在那里是因为代码很糟糕。

注释会导致我们写出糟糕的代码,其中包含我们在本章其余部分讨论过的所有腐烂味道。我们的第一个行动是通过重构来消除不良气味。当我们完成后,我们经常发现评论是多余的。

尽管有争议,但我读过的代码确实如此。公平地说,Fowler 并不是说​​永远不要发表评论,而是在做之前先考虑代码的状态。

于 2009-02-01T00:41:39.187 回答
12

您需要文档(以某种形式;并不总是注释)以在本地理解代码。如果您阅读了所有代码并牢记在心,代码本身就会告诉您它的作用。(更多内容见下文。)评论最适合非正式或半正式文档。

许多人说注释是一种代码味道,可以通过重构、更好的命名和测试来替代。虽然糟糕的评论(很多)都是如此,但很容易得出结论总是如此,哈利路亚,没有更多的评论。这将本地文档的所有负担——我认为太多了——放在了命名和测试上。

记录每个函数的契约,以及对于每种类型的对象,它表示什么以及对有效表示的任何约束(技术上,抽象函数和表示不变)。在可行的情况下使用可执行的、可测试的文档(文档测试、单元测试、断言),但也要写简短的注释,在有帮助的地方给出要点。(如果测试采用示例的形式,它们是不完整的;如果它们是完整的、精确的契约,它们可能与代码本身一样需要深入研究。)为每个模块和每个项目编写顶级注释;这些可以解释使您所有其他评论(和代码)保持简短的约定。(这支持命名为文档:建立了约定,并且我们可以期望在其中发现细微之处,我们可以更有信心地相信名称说明了我们需要知道的一切。)更长的、程式化的、令人恼火的冗余 Javadocs 有它们的用途,但是帮助产生了反弹。

(例如,这个:

执行 n 次 fobulation。
@param n 移动的次数
@param x 移动中心的 x 坐标
@param y 移动中心的 y 坐标
@param z 移动中心的 z 坐标

可能就像“围绕中心 (x,y,z) 摇晃 n 次”。评论不一定是阅读和写作的苦差事。)

我并不总是按照我在这里说的去做;这取决于我对代码的重视程度以及我希望谁阅读它。但是学习如何编写这种方式让我成为了一个更好的程序员,即使是在偷工减料的时候。

回到我们为了本地理解而记录的声明:这个函数有什么作用?

def is_even(n): return is_odd(n-1)

测试整数是否为偶数?如果is_odd()测试一个整数是否为奇数,那么是的,这有效。假设我们有这个:

def is_odd(n): return is_even(n-1)

同样的推理表明,这is_odd()会测试整数是否为奇数。当然,将它们放在一起,两者都不起作用,即使每个都起作用,如果另一个起作用。稍微改变一下,我们就会得到可以工作的代码,但只适用于自然数,而在本地看起来它仍然适用于整数。在微观世界中,这就是理解代码库的样子:循环追踪依赖关系,以尝试对作者可能已经解释过一两行的假设进行逆向工程。我讨厌在过去的几十年里,精神上粗心的编码人员的代价让我走上了这条路:哦,这种方法看起来有与扭曲核心远距离的副作用......总是?好吧,如果奇怪的crobuncles去饱和,至少;他们吗?最好检查所有 crobuncle 处理代码......这将对理解提出挑战。好的文档将这个 O(n) 指针追踪减少到 O(1):例如,知道一个函数的契约和它明确使用的事物的契约,函数的代码应该在不进一步了解系统的情况下有意义。(在这里,合同说is_even()is_odd()工作自然数会告诉我们两个函数都需要测试n==0。)

于 2009-02-01T05:20:11.760 回答
11

我唯一真正的规则是注释应该解释为什么代码在那里,而不是它在做什么或它是如何做的。这些事情可以改变,如果他们这样做,评论必须保持。代码最初存在的目的不应该改变。

于 2009-02-01T00:43:48.480 回答
9

注释的目的是解释上下文——代码的原因;这一点,程序员不能仅从代码检查中知道。例如:

strangeSingleton.MoveLeft(1.06);
badlyNamedGroup.Ignite();

谁知道这到底是为了什么?但通过一个简单的评论,一切都揭示了:

//when under attack, sidestep and retaliate with rocket bundles
strangeSingleton.MoveLeft(1.06);
badlyNamedGroup.Ignite();

说真的,评论是针对为什么,而不是如何,除非如何不直观。

于 2009-02-01T02:38:24.147 回答
8

虽然我同意代码应该是自读的,但我仍然认为添加大量注释块来解释设计决策很有价值。例如“我做了 xyz 而不是 abc 的常见做法,因为这个警告......”带有错误报告的 URL 或其他内容。

我试着把它看成:如果我已经死去,而一个刚从大学毕业的人必须在这里修复一个错误,他们需要知道什么?

于 2009-02-01T00:46:33.403 回答
6

一般来说,我看到用于解释写得不好的代码的注释。大多数代码都可以以使注释变得多余的方式编写。话虽如此,我发现自己在语义不直观的代码中留下了注释,例如调用具有奇怪或意外行为的 API 等......

于 2009-02-01T00:42:16.703 回答
6

我也普遍赞同self-documenting code的想法,所以我认为你的开发者朋友给出了很好的建议,我不会重复,但肯定有很多情况需要注释。

很多时候,我认为这归结为实现与未来代码阅读者将熟悉的普通或简单抽象类型的接近程度,或者更普遍地说,代码在多大程度上讲述了整个故事。根据编程语言和项目的类型,这将导致更多或更少的注释。

因此,例如,如果您在不安全的 C# 代码块中使用某种 C 风格的指针算法,则不应期望 C# 程序员轻松地从 C# 代码读取切换(这通常可能更具声明性或至少更少关于较低级指针操作)能够理解你的不安全代码在做什么。

另一个例子是当你需要做一些工作来推导或研究一个算法或方程,或者一些不会出现在你的代码中的东西,但有必要了解是否有人需要对你的代码进行重大修改。您应该在某处记录这一点,并且至少在相关代码部分中直接引用将有很大帮助。

于 2009-02-01T01:43:24.070 回答
5

我认为您的代码包含多少或多少评论并不重要。如果您的代码包含注释,则必须维护它们,就像您的其余代码一样。

编辑:这听起来有点自负,但我认为太多人忘记了即使变量的名称,或者我们在代码中使用的结构,都只是简单的“标签”——它们只对我们有意义,因为我们的大脑看到一串字符,例如customerNumber并理解它是一个客户编号。虽然注释确实缺乏编译器的任何“强制执行”,但它们并没有被删除。它们旨在向另一个人传达意义,一个正在阅读程序文本的人类程序员。

于 2009-02-01T00:35:38.387 回答
5

如果没有注释代码不清晰,先让代码表达更清晰的意图,然后只根据需要添加注释。

注释有它们的位置,但主要用于代码不可避免地微妙或复杂的情况(固有的复杂性是由于要解决的问题的性质,而不是由于程序员的懒惰或思维混乱)。

在代码行中要求注释和“衡量生产力”可能会导致垃圾,例如:

/*****
 *
 * Increase the value of variable i,
 * but only up to the value of variable j.
 *
 *****/

if (i < j) {
    ++i;
} else {
    i = j;
}

而不是简洁的(并且对于熟练的程序员来说很清楚):

i = Math.min(j, i + 1);

YMMV

于 2009-02-01T00:47:23.573 回答
4

我的绝大多数commnets都在类级别和方法级别,我喜欢描述更高级别的视图而不仅仅是args/return value。我特别小心地描述了函数中可能使粗心大意的人绊倒的任何“非线性”(限制、极端情况等)。

通常我不会在方法内部发表评论,除了标记“FIXME”项目,或者偶尔会出现一些我似乎无法清理的“这里是怪物”陷阱,但我非常努力地避免这些正如 Fowler 在Refactoring中所说,注释往往表明代码很小。

于 2009-02-01T00:45:05.340 回答
3

注释是代码的一部分,就像函数、变量和其他一切一样——如果更改相关功能,注释也必须更新(就像函数参数更改时函数调用需要更改一样)。

一般来说,在编程时,你应该只在一个地方做一次事情。

因此,如果通过清晰的命名来解释代码的作用,则不需要注释——当然这始终是目标——这是最干净、最简单的方法。

但是,如果需要进一步解释,我将添加一个注释,前缀为 INFO、NOTE 和类似的...
如果有人不熟悉该区域,则 INFO: 注释用于提供一般信息。
注意:注释是为了提醒潜在的异常情况,例如奇怪的业务规则/实现。
如果我特别不希望人们接触代码,我可能会添加一个 WARNING: 或类似的前缀。

我不使用并且特别反对的是变更日志样式的注释——无论是内联的还是文件头部的——这些注释属于版本控制软件,而不是源代码!

于 2009-02-01T00:46:51.983 回答
3

我更喜欢使用“Hansel and Gretel”类型的评论;代码中的小注释说明了我为什么要这样做,或者为什么其他方式不合适。下一个访问此代码的人可能会需要此信息,而且通常情况下,那个人就是我。

于 2009-02-01T01:41:24.120 回答
3

作为承包商,我知道有些维护我的代码的人会不熟悉我正在使用的 ADO.Net 的高级功能。在适当的情况下,我会添加一个关于我的代码意图的简短注释和一个指向 MSDN 页面的 URL,该页面会更详细地解释。

我记得在学习 C# 和阅读其他人的代码时,我经常被诸如“冒号字符的9 个含义中的哪个含义”之类的问题所困扰。如果您不知道该功能的名称,您如何查找它?!(旁注:这将是一个很好的 IDE 功能:我在代码中选择一个运算符或其他标记,右键单击然后显示它的语言部分和功能名称。C# 需要这个,VB 较少。)

至于“我不评论我的代码,因为它是如此清晰和干净”的人群,我发现有时他们高估了他们非常聪明的代码的清晰程度。复杂算法对于作者以外的其他人来说是不言自明的想法是一厢情愿的想法。

我喜欢@17 of 26 的评论(添加了重点):

...阅读代码会告诉你它做什么。但是,代码无法告诉您它 该做什么。

于 2009-02-01T03:28:11.097 回答
2

我很少评论。我的理论是,如果您必须发表评论,那是因为您没有以最好的方式做事。就像“变通”是我唯一要评论的事情。因为它们通常没有意义,但是您这样做是有原因的,因此您需要解释。

评论是低于标准代码 IMO 的症状。我坚信自我记录代码。我的大部分工作都可以很容易地翻译,即使是外行,因为描述性的变量名称、简单的形式、准确的方法和许多方法(IOW 没有做 5 种不同事情的方法)。

于 2009-02-01T00:51:41.210 回答
2

注释是程序员工具箱的一部分,可以被使用和滥用。这不取决于您、其他程序员或任何真正告诉您一个工具总体上不好的人。任何事情都有地点和时间,包括评论。

我同意这里所说的大部分内容,代码应该写得非常清楚,以至于它是自我描述的,因此不需要注释,但有时这与最佳/最佳实现冲突,尽管这可能可以解决一个适当命名的方法。

于 2009-02-01T00:52:00.337 回答
2

我同意自我记录代码理论,如果我不能通过阅读它来判断一段代码在做什么,那么它可能需要重构,但是有一些例外情况,如果出现以下情况,我会添加评论:

  • 我正在做一些你通常看不到的事情
  • 有不明显的主要副作用或实施细节,或明年不会出现
  • 我需要记住实现一些东西,尽管在这些情况下我更喜欢例外。
  • 如果我被迫去做其他事情并且我有很好的想法,或者代码遇到困难,那么我将添加足够的注释以暂时保持我的精神状态
于 2009-02-01T02:04:14.980 回答
1

大多数时候,我发现最好的评论是我目前正在编码的函数或方法名称。所有其他评论(除了你朋友提到的原因——我同意他们)感觉都是多余的。

所以在这种情况下,评论感觉有点矫枉过正:

/*
 * this function adds two integers
 */
int add(int x, int y)
{
    // add x to y and return it
    return x + y;
}

因为代码是自描述的。这种事情不需要评论,因为函数的名称清楚地表明了它的作用,return 语句也很清楚。当你把它分解成这样的小函数时,你会惊讶于你的代码变得多么清晰。

于 2009-02-01T00:45:07.993 回答
1

在使用 C 编程时,我会在头文件中使用多行注释来描述 API,例如函数的参数和返回值、配置宏等...

在源文件中,我将坚持使用单行注释来解释非不言而喻的代码片段的目的,或者将无法以理智的方式重构为更小的函数的子部分。这是我在源文件中注释的风格示例

如果您需要多于几行注释来解释给定代码的作用,那么您应该认真考虑您正在做的事情是否不能以更好的方式完成......

于 2009-02-01T01:03:28.207 回答
1

我写的注释描述了一个函数或方法的目的以及它返回的结果足够详细。我不会写很多内联代码注释,因为我相信我的函数和变量命名足以理解正在发生的事情。

我在许多写得非常糟糕的遗留 PHP 系统上进行开发。我希望最初的开发人员会在代码中留下某种类型的注释来描述这些系统中发生的事情。如果您要编写其他人最终会阅读的难以辨认或糟糕的代码,您应该对其进行注释。

另外,如果我正在以一种乍一看不正确的特定方式做某事,但我知道这是因为有问题的代码是平台或类似东西的解决方法,那么我会用 WARNING 评论进行评论.

于 2009-02-01T01:09:35.760 回答
1

有时代码完全可以完成它需要做的事情,但是有点复杂,并且在其他人第一次看到它时不会立即显而易见。在这种情况下,我将添加一个简短的内联注释来描述代码的用途。

我还尝试提供方法和类的文档标题,这对于智能感知和自动生成的文档很有用。实际上,我有一个坏习惯,就是将 90% 的方法和类都没有记录在案。当您处于编码过程中并且一切都在不断变化时,您没有时间记录事情。然后当你完成后,你不想回去寻找所有的新东西并记录下来。每个月左右回去写一堆文档可能会很好。

于 2009-02-01T03:43:00.130 回答
1

这是我的观点(基于几年的博士研究):

注释函数(使用某种黑盒,如 JavaDocs)和为将要阅读代码的人注释实际代码(“内部注释”)之间存在巨大差异。

大多数“写得好”的代码不应该需要太多的“内部注释”,因为如果它执行很多,那么它应该被分解成足够多的函数调用。然后在函数名称和函数注释中捕获每个调用的功能。

现在,函数注释确实是问题所在,在某些方面你的朋友是对的,对于大多数代码来说,没有像记录流行 API 那样完整规范的经济激励。这里重要的是确定什么是“指令”:指令是那些直接影响客户的信息片段,需要一些直接的操作(并且通常是意想不到的)。例如,X 必须在 Y 之前调用,不要从 UI 线程外部调用它,注意这有一定的副作用,等等。这些都是非常重要的要捕获的东西。

由于大多数人从不阅读完整的功能文档,并且略读他们所阅读的内容,因此您实际上可以通过仅捕获指令而不是整个描述来增加意识的机会。

于 2009-02-01T16:58:18.683 回答
0

我尽可能多地发表评论——然后,就像一年后我需要的那样。

于 2009-02-01T01:42:40.840 回答
0

我们添加了为所有公共类/方法/属性/等提供 API 参考文档的注释……这是非常值得的努力,因为 C# 中的 XML 文档具有为这些公共 API 的用户提供 IntelliSense 的良好效果。.NET 4.0 的代码契约将使我们能够进一步改进这种做法。

作为一般规则,我们不会在编写代码时记录内部实现,除非我们正在做一些不明显的事情。理论是,当我们正在编写新的实现时,事情正在发生变化,当尘埃落定时,评论很可能是错误的。

当我们重新开始处理现有的代码时,当我们意识到需要一些思考才能弄清楚到底发生了什么时,我们会添加注释。通过这种方式,我们最终得到了更可能正确的注释(因为代码更稳定)以及它们更有可能有用的地方(如果我今天回到一段代码,它似乎更多可能我明天可能会再回来)。

于 2009-02-01T02:55:53.220 回答
0

我的做法:

注释弥合了上下文/现实世界和代码之间的差距。因此,每一行都用正确的英语注释。

我确实拒绝在最严格的意义上不遵守此规则的代码。

格式良好的 XML 的使用 - 注释是不言而喻的。

马虎的评论意味着马虎的代码!

于 2010-06-02T13:21:42.147 回答
0

这是我编写代码的方式:

if (hotel.isFull()) {
    print("We're fully booked");
} else {
    Guest guest = promptGuest();
    hotel.checkIn(guest);
}

以下是我可能为该代码编写的一些评论:

// if hotel is full, refuse checkin, otherwise 
// prompt the user for the guest info, and check in the guest.

如果你的代码读起来像散文,那么写注释就没有意义,只是重复代码所读的内容,因为阅读代码所需的心理处理和注释几乎相等;如果您先阅读评论,您仍然需要阅读代码。

另一方面,在某些情况下,使代码看起来像散文是不可能或极其困难的。这就是评论可以修补的地方。

于 2010-10-09T22:52:21.197 回答