-
注释
注释毫无疑问是让别人以最快速度了解你代码的最快途径,但写注释的目的绝不仅仅是"解释代码做了什么",更重要的尽量帮助代码阅读者对代码了解的和作者一样多。
当你写代码时,你脑海里会有很多有价值的信息,但当其他人读你代码时,这些信息已经丢失,他们所见到的只是眼前代码。
-
注释约定
如果IDE提供注释格式,则尽量使用IDE提供的格式,否则使用"//"来注释。类、属性和方法的注释在Visual Studio中都使用输入"///"自动生成的格式。
-
类注释约定
/// <summary>
/// 类说明
/// </summary>
public class BinaryTree
-
类属性注释约定
/// <summary>
/// 属性说明
/// </summary>
public int NodesCount { get; private set; }
-
方法注释约定
/// <summary>
/// 方法说明
/// </summary>
/// <param name="parentNode">参数说明</param>
/// <returns>返回值说明</returns>
public int ComputeChildNodesCount(BinaryNode parentNode)
-
代码间注释约定
- 单行注释,注释行数<3行时使用
//单行注释
- 多行注释,2<注释行数<=10时使用
/*多行注释1
多行注释2
多行注释3*/
- 注释块,10<注释行数时使用,用50个 *
/***************************************************
* 代码块注释1
* 代码块注释2
* ......
* 代码块注释10
* 代码块注释11
***************************************************/
-
何时写注释的约定
- 以下三种情况我们需要在所有的类、类属性和方法都必须按照上述格式编写注释
- 客户方对代码注释重视程度较高
- 我们需要提供代码注释自动生成的API文档。
- 目前编写的是公共核心模块
- 如果客户方没有对注释特殊要求,那么按照下文中讨论的只在需要的地方加注释。不要加无谓的注释。
-
常用注释标识的约定
这里约定下以后团队常用几种注释标识及含义:
//TODO: 我还没有处理的事情
//FIXME: 已知的问题
//HACK: 对一个问题不得不采用比较粗糙的解决方案
//XXX: 危险!这里有重要的问题
请团队成员自行在Visual Studio中配置FIXME和XXX为高优先级的Comments.
Steps: Tools->Options->Environment->Task List->Tokens->Add->OK
配置完成后,我们就能在Task List(Ctrl+w,t)窗口中的Comments选项中看到代码中存在的任务了。
-
关于何时使用“///”和“//”的约定
a. 对于需要让调用者知道的信息,使用“///”注释,以便让调用者能在调用时看到。
b. 对于代码内部实现细节,需要维护者知道的注释,使用“//”。减少调用者阅读时间。
-
不需要的注释
阅读注释会占用阅读真实代码的时间,并且每条注释都会占用屏幕上的空间。所以我们约定所加的注释必须是有意义的注释,否则不要浪费时间和空间。
区别要不要写注释的核心思想就是:不要为那些能快速从代码本身就推断的事实写注释。
-
不要为了注释而注释
有些人可能以前的公司对于注释要求很高,如"何时写注释"章节中的要求。所以很多人为了写注释而注释。
再没有特殊要求的情况下我们要禁止写下面这种没有意义的注释。
/// <summary>
/// The class definition for Account
/// </summary>
public class BinaryTree
{
/// <summary>
/// Total counts of the nodes
/// </summary>
public int NodesCount { get; private set; }
/// <summary>
/// All the nodes in the tree
/// </summary>
public List<BinaryNode> Nodes { get; set; }
/// <summary>
/// Insert a node to the tree
/// </summary>
/// <param name="node">the node you want insert into the tree</param>
public void InsertNode(BinaryNode node)
-
不要用注释来粉饰糟糕的代码
写注释常见的动机之一就是试图来使糟糕的代码能让别人看懂。对于这种"拐杖式注释",我们不需要,我们要做的是把代码改的能够更具有"自我说明性"。
记住:"好代码>坏代码+好注释"
如下面这段函数的注释
//Enforce limits on the reply as stated in the request
//such as the number of items returned, or total byte size,etc.
public void CleanReply(Request request,Reply reply)
既然知道这个函数名会让人很难读懂,那么为什么不直接改好名字呢?这样所有调用这个函数的地方都能很快速知道这个函数的作用,不用再跟进来看函数的作用。
public void EnforceLimitsFromRequestOnReply(Request request,Reply reply)
-
日志式注释
有人喜欢在每次编辑代码时,都在模块开始处加一条注释。这类注释就像是一种记录每次修改的日志。在很久以前这种记录对于维护还有意义。但是对于现在的源码控制来说,这些记录完全是冗余的,需要完全废除。
/***************************************************
* July-29-2014:Fix Bug-12345: Add new method to calculate nodes count
* July-20-2014:Fix Bug-11111: Add Insert new node method
* ......
* July-20-2014:Task-00001: Create BinaryTree class
***************************************************/
-
个人签名
//Added By XXXX
有人认为这种注释有助于不了解这段代码含意的人和他讨论。事实上确是这条注释放在那一年复一年,后来的代码和原作者写的源码越来越不一样,和XXXX也越来越没关系。
重申一下,TFS里都能看到这类信息,不要加在代码里。
-
位置标识
//AddNodePlace1
//AddNodePlace2
有人喜欢在代码注释里加入位置标识以方便他查找代码的位置。
现在的IDE都集成了这些功能,如VS中可以使用Bookmark(Ctrl+b,t)。
不要将这类注释加到代码中。
-
注释掉的代码
直接把代码注释掉是非常令人讨厌的做法。
其他人不敢删掉这些代码。他们会想代码依然在这一定是有原因的,而且这段代码很重要,不能删除。而且每个阅读代码的人都会去看一下这些被注释掉的代码中是否有他们需要注意的信息。
这些注释掉的代码会堆积在一起,散发着腐烂的恶臭。
-
需要的注释
-
记录你对代码有价值的见解
你应该在代码中加入你对代码这段代码有价值的见解注释。
如: //出乎意料的是,对于这些数据用二叉树比哈希表要快40%
//哈希运算的代价比左右比要大的多
这段注释会告诉读者一些重要的性能信息,防止他们做无谓的优化。
-
为代码中的不足写注释
代码始终在演进,并且在代码中肯定会有不足。
要把这些不足记录下来以便后来人完善。
如当代码需要改进时:
//TODO:尝试优化算法
如当代码没有完成时:
//TODO:处理JPG以外的图片格式
你应该随时把代码将来该如何改动的想法用注释的方式记录下来。这种注释给读者带来对代码质量和当前状态的宝贵见解,甚至会给他们指出如何改进代码的方向。
-
对意料之中的疑问添加注释
当别人读你的代码的时候,有些部分可能让他们有这样的疑问:"为什么要这样写?"你的工作就是要给这些部分加上注释。
如:
// 因为Connection的创建很耗费资源和时间,而且需要多线程访问,
// 所以使用多线程单例模式
public static Connection Instance
{
get
{
if(_instance==null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = new Connection();
}
}
}
return _instance;
}
}
-
公布可能的陷阱
当为一个函数或者类写注释时,可以这样的问自己:"这段代码有什么出人意料的地方吗?会不会被无用?"。基本上说就是你需要未雨绸缪,预料到别人使用你代码时可能遇到的问题。如:
//XXX: 因为调用外部邮件服务器发送邮件,所以耗时较长,请使用异步方法调用以防止UI卡死。
public void SendEmail(string to, string subject, string body)
-
对于代码块总结性地注释
对于代码块的总结性注释可以使读者在深入细节之前就能得到该代码块的主旨,甚至有时候都可以直接跳过该代码块,从而可以快速准确的把握代码。
如读者看到://下面代码使用了二分查找算法来快速的根据用户Id找到相应用户
那么他就可以快速理解下面代码的逻辑,否则自己看二分查找还是要用些时间的。