你评论你的代码?
我通常评论“ifs”,并用“人类语言”写下它的意思,比如“检查是A还是B”。
我发现读初级代码的初级程序员更好地阅读它的含义,然后分析语句(当我检查旧代码时也是这样)
你是做什么? 其他情况呢? 优点? 缺点?
杰夫有一个关于这个问题的真棒post 。 在post中的链接和一些博客的反应也值得一读。
评论不应该是“如何”完成,而是“为什么”完成(除非在非常罕见的情况下,从代码中看不太明显,我的意思是超级稀有)。
“自我logging代码”理论存在一个悲剧的缺陷。 是的,阅读代码会告诉你它到底在做什么。 但是,代码不能告诉你应该做什么。
我认为可以肯定地说,所有的错误都是在代码没有完成它应该做的事情时造成的。 因此,如果我们添加一些关键的注释,为维护人员提供足够的信息来知道代码应该做什么,那么我们已经给他们修复了大量的错误。
这给我们留下了多less评论的问题。如果你提出太多的评论,事情就变得冗长乏味,评论将不可避免地与代码过时。 如果你投入太less,那么他们不是特别有用。
我发现定期评论在以下地方最有用:
1).h或.cpp文件顶部的简要描述,用于解释类的用途
2)在实现一个非平凡函数之前的一个注释块,解释它的目的,并详细说明它的期望input,潜在输出以及调用函数时期望的任何古怪。
除此之外,我倾向于评论任何可能使某人感到困惑或奇怪的东西。 例如:“这个数组是基于1的,而不是基于因为blah blah的0”。
我通常评论“ifs”,并用“人类语言”写下它的意思,比如“检查是A还是B”。
如果你发现自己这样做了,重构可能会更好,这样复杂的逻辑逻辑就被提取到另一个方法中,或者至less引入一个名称variables来清楚地表明逻辑的含义。
评论的问题是,他们(有时)违反干(不要重复自己)的指导方针。 当您通过将其添加到文档中来复制逻辑时,即使是代码旁边的注释,也会冒着过时的风险。 不久,评论可能会变得不完整或不准确。
这就是说,我不介意看评论,即使他们有点过时了。 我遇到了如下代码:
// collect data Data data = collectData(); // analyze data Report report = analyze(data); // print report report.print();
这些types的评论对每个人都是毫无价值的,我通常会在视线上删除它们。
我已经学会了编写最低公分母的难题。 这包含了很多行为。
-
注释代码块,不一定每行。 请解释为什么下面的代码块(或代码段)是按照它的方式编写的。 在评论中joindate和首字母缩写,以便知道何时发表评论以及评论的撰写者。 尽量避免像“这是愚蠢的”或“我非常聪明”这样的评论,因为它实际上没有帮助,将来可能被certificate是错误的。 如果您使用任何花哨的技巧或做一些不明显的事情,请解释一下为什么。
-
使用人类可读的variables名称。 这意味着避免像鼠疫这样的单字母variables。 如果索引variables被称为rowIndex比i更容易猜测循环的作用。 几乎所有的文明语言都有一个编译器或解释器,在把你的工作放入机器代码之前,会清理掉variables名,所以使用神秘的variables名是没有任何好处的。
-
记住编译器是聪明的。 很聪明。 每个现代编译器都知道如何优化循环和其他基本的东西。 在一行上写一个完整的while循环可能看起来很喜欢你,但编译完成后会让其他人看着你的代码(如果你在几个星期后回到代码中,就算你自己了)。 避免花哨的语言技巧,如果他们使代码更不可读。
-
采取一种风格,坚持下去。 总是以相同的格式(和相同的缩进)执行if,loop等。 您将能够更轻松地发现代码的各种function,因为相同的function看起来是一样的。 如果你犯了一个错误,它会突出一点。
做这些事情使得代码对每个人都更可读。 采用良好的编码习惯也减less了对评论的依赖,所以这是双赢的。
如果一段代码需要评论。 这可能需要重构。
例如
void MyMethod() { //set the UI myTextbox.text = "Foo"; myOthertextbox.text = "Bar"; foreach (n in thing) //something other stuff }
应该可能被重构来提取UI的东西。
void MyMethod() { SetUI(); foreach (n in thing) //something other stuff } void SetUI() { myTextbox.text = "Foo"; myOthertextbox.text = "Bar"; }
除了方法和属性等的描述,我通常不会评论我的代码,除非有一点特别难以理解。 我个人的目标是编写一些我可以在一年之内回来的代码,并且在大约5分钟内做出判断,而且我通常会达到这个目的。 最好的代码很容易理解,它不需要大量的评论。
代码中散布的代码的问题是,当代码改变(代码将改变)时,评论通常不会,或者至less不一致。 这导致更令人困惑,不可维护的代码,因为经过几次更改后,评论和代码不会说几乎相同的事情。
相反,我更喜欢每个模块(类,function,无论)都有评论。 这个评论部分应该用简单的英文解释模块的function是什么。
如果我认为一段代码是非常棘手的,那么我将它重构成它自己的函数,所以函数名和相关的注释可以解释它应该做什么。
理想情况下 ,代码应该是“自我logging”,但请忽略那些说“如果代码不是自我logging,而且写得不好”的人。 在现实世界中,这并不总是可能的,这取决于代码的复杂性。
显然这并不意味着你应该把注释放在任何地方 ,或者有类似的东西
option = option->next; // get the next option i++; // increment i
但是如果一段代码足够复杂,那么在那里描述它应该做什么的评论远比在其他地方去找一些希望最新的devise文档更有价值。
我意识到这是陈词滥调,但代码应该logging自己。 为了清楚起见,我倾向于在声明更多variables方面犯错(假设编译器可以/将优化不必要的variables)。 保持function简短,variables和function名称。 我试图做到这一点,然后只评论棘手的东西。
但请注意,较短的函数使得难以看到代码作为一个整体如何协同工作。 loggingfunction时要记住这一点(而不是单独的代码行)。 在function甚至单元层面,你应该loggingfunction如何相互作用。
尽量不要评论代码。 loggingAPI,但努力编写不需要评论的代码,而是从variables,函数名称等方面来理解。这里已经有几个很好的答案。 当然,当你做一些深奥的甚至有趣的事情时,有时候你必须在代码中加注释。
但是,这里有一些额外的东西,如何不发表评论。
-
不要在您的评论中添加任何创build/修改date。 版本信息头是好的,但不是必需的,可能不是最好的主意。 你的VCS应该照顾这个。 如果您在需要注释的代码上没有使用VCS,那么您做错了。 学习使用一个(我build议git或Mercurial),直到它成为你的第二天性。
-
FIXME甚至更多的TODO不属于代码,也不在评论中。 错误跟踪器是正确的地方。 或者至less一个顶级的TODO文件。
-
保持你的平凡技能给自己。 每个人都知道你是一个触摸式的打字员,可以在相同的时间内写三页; 但是当我读代码的时候,我阅读代码,并且不愿意打乱我的想法。
-
不要试图在注释中解释系统的体系结构。 即使API文档并不总是最好的地方。 如果整个thig是真的那么复杂,使用一个更容易阅读的外部格式正确的文件,最好使用pdf或html格式。
只要记住:源文件是代码的作用。 他们既不这样做,也不是为什么这样做。 如何进入API文档(严格意义上我不认为是“评论”),以及程序文档中的原因。 不要将文档与评论混淆。 你的pipe理层会为此感谢你。
(如果你正在考虑文字编程,这是另一个问题,我没有足够的经验给你任何build议。)
我不评论明显的东西(即“设置为2”或“检查是否大于5”),但有时只是一些有关代码的使用信息,即“构build外部表”或一些奇怪的,如“MyClass.Count实际上是计数1”。 当然,还有一些针对bug的解决方法,比如“KB123456的解决方法 – 解释错误的某个网站的url….”
所以基本上:我几乎从来没有评论“什么”,但有时是“为什么”。
代码本身应该能够解释它如何工作,没有任何意见。 伴随代码的评论应该解释为什么它的工作原理。
你应该能够通过一个没有评论的函数的代码,并找出它在做什么,但没有评论,你永远不会明白为什么它会做什么。 写得很好的注释解释了函数如何与大局相联系,以及一些日常的编码决定 – 在条件语句中包装代码块的原因,或者使用try {赶上看似无害的一套陈述。
解释性的评论和使用明确的variables/方法/类是齐头并进的。 你不会完全明白,直到你被遗弃在一个遗留系统上,没有现有的工作人员的工作,没有文件。
在我目前的工作中,我做了以前没有做过的评论 – 我们正在将VB6转换为VB .NET,而公司的另外两名程序员对真正的面向对象语言几乎没有经验。 我做了很多意见作为就地教程types的东西。 是的,这些信息可能在外部文件,甚至在一本书或在线教程中,但存在很多信息! 我用相关的小剂量给它。
我觉得对初级程序员来说更好
如果你严重质疑写评论的价值,那么我也必须把你包括在“初级程序员”这个小组中。
评论绝对至关重要。
复杂的代码必须注释。 你真的相信你写的所有代码对所有读者都是直观明了的吗?
不,不是。 评论你的代码。
是。 我广泛评论。 在OO不常见的情况下,操作系统是一个梦想,而图书馆则是一个神话。
因此, 当我为embedded式系统编写驱动程序时 ,我必须发表评论,使我的小心谨慎的代码易读,并在下周阅读。 这也有帮助,因为有大块的代码我没有写和没有文档, 因此我得到写评论如何谈谈我的代码,以接口。
所以,尽pipe你们的其他专家可能如此出色,你可以阅读未注释的C# – 我不够聪明来阅读我的未注释的C代码。 所以我发表评论,我的代码工作得很好。 🙂
我logging了函数做了什么,它的参数和它返回什么以及为什么。 在函数内部,我很less评论任何东西。 我发现,如果variables名称select的很好,那么他们都是真正需要的评论。
我现在主要编写C#代码,但是我发现这个系统几乎适用于任何面向对象的语言,因为良好分解的OO中的函数通常不会超过五十行。 如果他们这样做的话,我可能会在这个过程的文档小节中提出一些意见,但是在那个时候,我通常把这个单一function分解成子function。
我只尝试评论必要的事情。 像这个类的目的是什么,这个方法做了什么(如果不是很明显的话),哪些值可以是参数等。我尽量避免描述代码的注释。 相反,我试图让代码自我描述。 从大块代码中提取更小的方法等等。当然,这并不总是可能的,有时甚至会使其变得更糟。 您必须find代码和注释组合最易读的平衡点。 你可以做的最糟糕的事情就是给getters和setter这样的明显的方法添加注释,当它们做的不是超过他们应该做的 – 获取或设置指定的字段。 有一点对于评论来说是非常危险的,那就是当实施改变并且评论被遗忘时,他们可能会过时。 所以你应该试着把注意力集中在有趣的东西上,因为如果你的代码没有源代码,那么你的代码或者你自己的用户将会看到并且必须信任它们;)
我通常只是在两种情况下发表评论。
- 在描述其他人使用的API中的方法或类的function时。
- 当一段代码正在做一些并不明显的事情时。
当然,我总是试图给我的variables和函数有意义的名称,并尽量减less评论的方式。
“我看不到所有评论的代码”,是我在职业生涯中多次听到的陈述或类似的陈述。 这是一个警告,太多的评论花费太多的时间来阅读和放慢生产力。
所以,总的来说,我把我的评论限制在总结中。 那就是我将给出一个类的总结及其意图。 我将为每个方法/属性提供一个句子摘要,我将详细解释复杂的代码区域。 我很less使用在线评论,因为它分散了程序逻辑的stream程和可读性。
我计划的时间越长(嗨,我刚刚进入了我的第四个十年),我写的评论越less。 我经常重构我的代码,使其更加明了,因为这比写评论来解释它更简单,更安全。
因此,我发现,当我回去阅读旧代码时,我写的评论几乎总是值得一读的。
即使在代码不言自明的情况下,我也可以在方法文档中编写注释,这样IntelliSense就可以显示出来。 即使这些注释在100%的时间内可能不是有用的,但是如果我将鼠标hover在方法调用上并且没有任何东西popup,这只是令人讨厌的。
我试图删除移动评论分开的function,似乎function越来越大。
为了更快地看大,我使用了像14或15这样的大字号。
我只是在代码被“完成”的时候才作为服务给别人评论。 当我在自己的项目上工作时,我很less打扰,因为我宁愿添加function和修复错误,而不是写一些我可能永远不会读的东西。
有趣的是,在阅读其他人的代码时,我经常删除或忽略注释,直接看代码。 我发现很多情况下可以比相应的评论更清楚地阅读。
我相信在例程/方法/模块的开始,您将logging整个代码的原因/意图。 包括对变化的评论(谁,何时,为什么/什么)是很好的。 在代码本身中,可能不清楚的东西或做出的假设应该被评论。
硬编码的数字不要与使用有意义名称的常量进行比较。
我不得不把代码重构成一两行的方法,看起来有意义的名字,但实际情况是,应该有一些评论来给出关于正在发生的事情以及与其他事情有关的方法继续。
就像@布拉德 – 威尔逊说,为什么不怎么样。 但是我也在我的代码中使用了TODO,但是为了生产,我试图限制这些。
转到代码完整副本的第32章“自行归档代码”。
一位导师曾经与我分享过一句古老的谚语,尽pipe我不知道原文的来源:
好的程序员评论他们的代码。
伟大的程序员告诉你为什么select一个特定的实现。
主程序员告诉你为什么没有select其他的实现。
或者这个效果的话…
在这个讨论中应该更清楚地强调的一点是代码注释和API文档之间的区别。
由于现代文档生成器通过parsing代码中的注释来工作(例如,C#的XML格式的文档注释),所以一些开发人员可能在思想上混淆了注释和文档。 针对此线程和其他地方显示的评论代码的一般态度可能会导致这些开发人员不logging他们的API。
虽然我同意最好是使代码清晰而不是评论,但我也相信logging您的接口是非常重要的。 文档也不应该太冗长。 它应该只包含对使用界面的人有价值的信息。 它不应该包含任何不必要的实现细节。 但是所提供的信息应该是完整的,以便用户不必看着你的源代码来使用你的模块。 出现在IDE的工具提示中的文档或其他地方出现的较长的文档应该是所有必需的。
这种方法鼓励封装,因此是良好的编程实践。
/// <summary> /// Gets the name of a seventeenth century philosopher. The name is /// chosen randomly among those that are stored in the database, /// whose primary cultural contribution was published between 1601 /// and 1700. Names may be repeated on multiple calls. The /// ConnectionString property must be set before calling this method. /// </summary> /// <exception cref="InvalidOperationException">The ConnectionString /// property was not set.</exception> /// <exception cref="SqlException">A problem occured with the database /// connection.</exception> public string GetRandomSeventeenthCenturyPhilosopherName() { ...
我会经常对我的评论进行date戳,并描述为什么我正在做某件事,而不是如何(毕竟,代码如何)。
如果我正在进行更改,特别是在用户驱动的情况下,我不会删除旧的代码,而是将其注释掉,并将其删除。 这就像一个“就地”的版本系统,只是它允许我在修改代码时阅读历史logging。
-R