c#编码注释

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1      目录node

2       前言... 3程序员

2.1        编写目的... 3算法

2.2        适用范围... 4数据库

3       命名规范... 4c#

3.1        命名约定... 4数组

3.1.1         PascalCasing. 4缓存

3.1.2         camelCasing. 4安全

3.1.3         UPPER_CAPS. 4服务器

3.1.4         私有变量的命名... 4多线程

3.1.5         首字母缩写词的大小写... 4

3.1.6         复合词的大小写... 5

3.2        命名选择... 5

3.2.1         名字必定要可以表达出标识符的含意... 5

3.2.2         命名要与使用者的指望相匹配... 6

3.2.3         不要卖弄风骚... 6

3.3        命名最佳实践... 6

3.3.1         命名空间... 6

3.3.2         要让接口的名字以字母I开头... 6

3.3.3         派生类的末尾使用基类名称... 7

3.3.4         泛型类型参数的命名... 7

3.3.5         枚举类型的命名... 7

3.3.6         属性的命名... 7

3.3.7         事件的命名... 7

3.3.8         字段的命名... 8

4       注释... 8

4.1        注释约定... 8

4.1.1         类注释约定... 8

4.1.2         类属性注释约定... 8

4.1.3         方法注释约定... 8

4.1.4         代码间注释约定... 9

4.1.5         强制注释的约定... 9

4.2        不须要的注释... 9

4.2.1         不要为了注释而注释... 10

4.2.2         不要用注释来粉饰糟糕的代码... 10

4.2.3         日志式注释... 10

4.2.4         我的签名... 11

4.2.5         位置标识... 11

4.2.6         注释掉的代码... 11

4.3        须要的注释... 11

4.3.1         记录你对代码有价值的看法... 11

4.3.2         为代码中的不足写注释... 11

4.3.3         对意料之中的疑问添加注释... 12

4.3.4         公布可能的陷阱... 12

4.3.5         对于代码块总结性地注释... 12

4.4        如何写好注释... 13

4.4.1         避免使用不明确的代词... 13

4.4.2         精确描述方法的行为... 13

4.4.3         用输入输出例子来讲明特殊的状况... 13

4.4.4         更新代码时记得更新注释... 14

4.4.5         只有能让别人读懂的注释才是合格的注释... 14

4.5        region的使用... 14

4.6        c#中巧用#if debug进行调试... 14

4.7        c#特性代码简洁... 14

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2      前言

2.1    编写目的

为了保证你们编写出的程序都使用统一的风格,以方便阅读和后期维护。

编码规范对于程序员而言尤其重要,有如下几个缘由:

  1. 一个软件的生命周期中,80%的花费在于维护。
  2. 几乎没有任何一个软件,在其整个生命周期中,均由最初的开发人员来维护。
  3. 编码规范能够改善软件的可读性,可让程序员尽快而完全地理解新的代码 。
  4. 编码规范能够保证代码阅读者在阅读代码时产生尽量少的歧义。
  5. 编码规范可使咱们的代码统1、美观,让客户能够一眼看出咱们是专业的团队。

 

编码规范的核心出发点是

  1. 让其余人可以快速,准确的知道代码的做用而且确保不会出现歧义(其余人也包括一段时间后的本身)
  2. 让咱们的代码看着更加清晰、美观、专业、统一。

2.2    适用范围

本公司内部开发人员

 

3      命名规范

任何标识符的名字都应该能够简单、清楚、正确的表示出该标识符的做用。

同时咱们要将尽量多的信息装入到标识符的名字当中去,以便读代码的人能够快速的读懂代码。

3.1    命名约定

咱们在命名标识符时(包括参数,常量,变量),应使用单词的首字母大小写来区分一个标识符中的多个单词,如UserName.

3.1.1    PascalCasing

PascalCasing包含一到多个单词,每个单词第一个字母大写,其他字母均小写。例如:HelloWorld、SetName等。

除了参数、变量、常量外,全部命名空间名称、类、函数、接口、属性、事件、枚举等名称的命名,使用 Pascal 风格。

3.1.2    camelCasing

camelCasing包含一到多个单词,第一个单词首字母小写,其他单词首字母大写。例如:name、productId等。

参数与变量的命名使用camelCasing.

3.1.3    UPPER_CAPS

UPPER_CAPS包含一到多个单词,每一个单词的全部字母都大写,单词与单词之间用”_”链接,该风格目前在c#中只用于const常量。

如:public const string DEFAULT_PAGE = "default.aspx";

3.1.4    私有变量的命名

Private 的私有变量使用下划线”_”+camelCasing的大小写规则,以便快速确认该变量的做用域。

如:  private int _userId;

3.1.5    首字母缩写词的大小写

首字母缩写词是由一个短语的首字母组成的,如Xml(ExtensibleMarkuLaguage),IO(Input and Output)。它和单词缩写是有区别的,单词缩写仅仅是把一个单词的长度变短。

  1. 把两个字母的首字母缩写词所有大写,除非它是camelCasing的第一个单词。

using System.IO;

public void StartIO(Stream ioStream)

  1. 由三个或以上的字母组成的首字母缩写词,只有第一个字母大写,如Xml,Html.除非首字母是camelCasing标识符的第一个单词。

using System.Xml;

public void ProcessXmlNode(XmlNode xmlNode)

3.1.6    复合词的大小写

不要把复合词中的首字母大写。复合词要当成一个单词来处理。

如endpoint, callback,metadata,namespace等都是正确的写法

3.2    命名选择

3.2.1    名字必定要可以表达出标识符的含意

标识符名字必需要表达出该标识符的意义,绝对不可使用无心义的v1,v2…vn之类的命名。

        public static void CloneChars(char[] cl1, char[] cl2)

        {

            for (var i = 0; i < cl1.Count(); i++)

            {

                cl2[i] = cl1[i];

            }

        }

代码的调用者不看这函数是没法知道cl1仍是cl2是要拷贝的char数组,他必须进到这个函数去看完整个逻辑才能够调用。并且在看的过程当中cl2[i] = cl1[i]; 也须要他花几秒钟来思考是作什么的。

若是改为有意义的名字: source 和target那么这个方法调用者一看名字就知道使用方法了。

   public static void CloneChars(char[] source, char[] target)

        {

            for (var i = 0; i < source.Count(); i++)

            {

                target[i] = source[i];

            }

        }

 

在给标识符命名时,必定不能产生歧义,代码中的不少错误都是因为命名时的歧义形成的。例如:

public const int CART_TOO_BIG_LIMIT = 10;

if (ShoppingCart.Count() >= CART_TOO_BIG_LIMIT)

            {

                LogError("Too many items in cart.");

            }

3.2.2    命名要与使用者的指望相匹配

有些名字之因此会让人误解是由于带吗阅读者对它们有先入为主的印象,就算你本意并不是如此。这种状况下,你最好是选用一个与使用者指望所匹配的名字。

如不少程序员都习惯了把Get开始的方法看成“轻量级访问器“,他只是简单的返回成员变量。

你们看到如下的代码

    class BinaryTree

    {

        public int GetNodesCount()

会觉得只是返回内部private int _nodesCount; 私有变量的访问器。

但若是实际你的代码多是一个很是耗时的代码,内部实现是广度优先遍历全部的树节点,还要去数据库查找父节点和子节点的关系,而后累加。

那么这么一个耗时的方法可能因为你的命名,致使了被调用者反复屡次的调用,致使整个系统性能降低。

若是你将命名改成ComputeNodesCount那么调用者就会知道这是个耗时的操做,须要缓存调用结果并减小调用。

3.2.3    不要卖弄风骚

使用最经常使用,众所周知的单词。不要在代码命名时卖弄你的学识,要让你的代码快速准确的表达出你的想法才是真正的牛人。

如public static string ConvertXml2Html (string sourcePath)

有些人在看到这个方法的时候怎么想也想不明白这个2是作什么用的,是把一个Xml文件变成两个Html?

熟悉英语文化的人可能知道这是To的俚语表达。若是你不能保证全部阅读你代码的人都知道2是To的缩写。那么请使用ConvertXmlToHtml命名。

 

3.3    命名最佳实践

3.3.1    命名空间

  1. 要使用PascalCasing,并用点号来分隔名字空间中的各个部分。

如Microsof.Office.PowerPoint

3.3.2    要让接口的名字以字母I开头

如IComponet,IDisposable 你们一看就知道是接口。

同时要确保若是一个类是一个接口的标准实现,那么这个类和接口应该只差一个”I“前缀。

3.3.3    派生类的末尾使用基类名称

例如,从 Stream 继承的 Framework 类型以 Stream 结尾,从 Exception 继承的类型以 Exception 结尾。

3.3.4    泛型类型参数的命名

  1. 使用描述性的名字来命名泛型类型参数,而且在前面加上T前缀

以下面都是很好的命名

public delegate TOutput Converter<TInput, TOutput>(TInput from);

  1. 若是只有一个类型参数,能够只用一个字母T来表示泛型

public class Nullable<T>

public class List<T>

  1. 若是泛型参数有约束,那么须要在泛型类型参数名中须要显示出该约束

public interface ISessionChannel<TSession> where TSession:ISession

3.3.5    枚举类型的命名

  1. 要用单数名词而不是复数命名枚举类型,如要用ConsoleColor而不是ConsoleColors

public enum ConsoleColor

    {

        Red,

        Yellow,

        Blue

}

  1. 不要给枚举类型加”Enum“、”Flag”等后缀。

ColorEnum,ColorFlag都很差,由于自己就是枚举,再加上就是没有意义的重复 。

3.3.6    属性的命名

  1. 要用名词、名词短语或形容词来命名属性
  2. 要用描述集合中具体内容的短语的复数形式来命名属性集合,而不要用短语的单数形式加”List“、”Array”或”Collection“后缀

class BinaryTree

    {

        //Good Naming

        public NodeCollection Nodes { get; set; }

 

        //Bad Naming

        public NodeCollection NodesCollection { get; set; }

  1. 要用确定性的短语命名布尔属性。最好在前面选择性的加入”Is“、”Can“、”Has“等前缀。

CanSeek比CantSeek和Seekable都更准确和容易理解。

3.3.7    事件的命名

  1. 要用动词或动词短语命名事件

如: Clicked、Painting、DroppedDown 等等

  1. 要用如今进行时(ing)和过去式(ed)来赋予事件发生以前和以后的概念。而不是使用Before和After.

如窗口关闭前发生的close事件应该命名为Closing,而在窗口关闭以后发生的应该命名为Closed.

3.3.8    字段的命名

  1. 禁止使用实例的公有字段和受保护字段,请使用属性代替。

Tips:在VisualStudio中输入”prop”可快速建立外部可修改的属性,输入”propg”可快速建立不容许外部修改的属性。如:

        //prop

        public int NodesCount { get; private set; }

        //propg

        public List<BinaryNode> Nodes { get; set; }

  1. 通常只使用静态字段
  2. 要使用名词、名词短语或形容词命名字段
  3. 不要给字段加前缀如“g_”、”s_”来表示静态字段。由于字段和属性是很是类似的,因此要遵循相同的命名规范。

 

4      注释

注释毫无疑问是让别人以最快速度了解你代码的最快途径,但写注释的目的毫不仅仅是”解释代码作了什么“,更重要的尽可能帮助代码阅读者对代码了解的和做者同样多。

当你写代码时,你脑海里会有不少有价值的信息,但当其余人读你代码时,这些信息已经丢失,他们所见到的只是眼前代码。

4.1    注释约定

若是IDE提供注释格式,则尽可能使用IDE提供的格式,不然使用”//”来注释。类、属性和方法的注释在Visual Studio中都使用输入”///”自动生成的格式。

4.1.1    类注释约定

///<summary>

    ///角色信息

    ///</summary>

    public  class  CarRoleModel

4.1.2    类属性注释约定

///<summary>

     ///角色id

     ///</summary>

     publicstring RoleId { get; set; }

4.1.3    方法注释约定

///<summary>

     ///用户登陆

     ///</summary>

     ///<param name="userName">用户名</param>

     ///<param name="password">密码</param>

     ///<returns>返回用户登陆结果</returns>

     public ActionResult SubmitLogin(string userName, string password)

4.1.4    代码间注释约定

  1. 单行注释,注释行数<3行时使用

//单行注释

  1. 多行注释,2<注释行数<=10时使用

/*多行注释1

多行注释2

多行注释3*/

  1. 注释块,10<注释行数时使用,用50个*

/***************************************************

         *  代码块注释1

         * 代码块注释2

         * ......

         * 代码块注释10

         * 代码块注释11

***************************************************/

4.1.5    强制注释的约定

  1. 如下三种状况咱们须要在全部的类、类属性和方法都必须按照上述格式编写注释

1)  客户方对代码注释重视程度较高

2)  咱们须要提供代码注释自动生成的API文档。

 

3) 目前编写的是公共核心模块

  1. 若是客户方没有对注释特殊要求,那么按照下文中讨论的只在须要的地方加注释。不要加无谓的注释。

4.2    不须要的注释

 

阅读注释会占用阅读真实代码的时间,而且每条注释都会占用屏幕上的空间。因此咱们约定所加的注释必须是有意义的注释,不然不要浪费时间和空间。

区别要不要写注释的核心思想就是:不要为那些能快速从代码自己就推断的事实写注释。

4.2.1    不要为了注释而注释

有些人可能之前的公司对于注释要求很高,如“什么时候写注释”章节中的要求。因此不少人为了写注释而注释。

再没有特殊要求的状况下咱们要禁止写下面这种没有意义的注释。

    /// <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)

4.2.2    不要用注释来粉饰糟糕的代码

写注释常见的动机之一就是试图来使糟糕的代码能让别人看懂。对于这种“拐杖式注释”,咱们不须要,咱们要作的是把代码改的可以更具备”自我说明性“。

记住:“好代码>坏代码+好注释”

以下面这段函数的注释

        //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)

4.2.3    日志式注释

有人喜欢在每次编辑代码时,都在模块开始处加一条注释。这类注释就像是一种记录每次修改的日志。在好久之前这种记录对于维护还有意义。可是对于如今的源码控制来讲,这些记录彻底是冗余的,须要彻底废除。

         /***************************************************

         *    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

        ***************************************************/

4.2.4    我的签名

//Added By XXXX

有人认为这种注释有助于不了解这段代码含意的人和他讨论。事实上确是这条注释放在那一年复一年,后来的代码和原做者写的源码愈来愈不同,和XXXX也愈来愈不要紧。

重申一下,TFS里都能看到这类信息,不要加在代码里。

4.2.5    位置标识

        //AddNodePlace1

        //AddNodePlace2

有人喜欢在代码注释里加入位置标识以方便他查找代码的位置。

如今的IDE都集成了这些功能,如VS中可使用Bookmark(Ctrl+b,t)。

不要将这类注释加到代码中。

4.2.6    注释掉的代码

直接把代码注释掉是很是使人讨厌的作法。

其余人不敢删掉这些代码。他们会想代码依然在这必定是有缘由的,并且这段代码很重要,不能删除。并且每一个阅读代码的人都会去看一下这些被注释掉的代码中是否有他们须要注意的信息。

这些注释掉的代码会堆积在一块儿,散发着腐烂的恶臭。

4.3    须要的注释

4.3.1    记录你对代码有价值的看法

你应该在代码中加入你对代码这段代码有价值的看法注释。

如:    //出乎意料的是,对于这些数据用二叉树比哈希表要快40%

        //哈希运算的代价比左右比要大的多

这段注释会告诉读者一些重要的性能信息,防止他们作无谓的优化。

4.3.2    为代码中的不足写注释

代码始终在演进,而且在代码中确定会有不足。

要把这些不足记录下来以便后来人完善。

如当代码须要改进时:

//TODO:尝试优化算法

如当代码没有完成时:

//TODO:处理JPG之外的图片格式

你应该随时把代码未来该如何改动的想法用注释的方式记录下来。这种注释给读者带来对代码质量和当前状态的宝贵看法,甚至会给他们指出如何改进代码的方向。

4.3.3    对意料之中的疑问添加注释

当别人读你的代码的时候,有些部分可能让他们有这样的疑问:“为何要这样写?”你的工做就是要给这些部分加上注释。

如:      

        // 由于Connection的建立很耗费资源和时间,并且须要多线程访问,

        // 因此使用多线程单例模式

        public static Connection Instance

        {

            get

            {

                if(_instance==null)

                {

                    lock (_lock)

                    {

                        if (_instance == null)

                        {

                            _instance = new Connection();

                        }

                    }

                }

                return _instance;

            }

        }

4.3.4    公布可能的陷阱

当为一个函数或者类写注释时,能够这样的问本身:”这段代码有什么出人意料的地方吗?会不会被无用?“。基本上说就是你须要未雨绸缪,预料到别人使用你代码时可能遇到的问题。如:

        //XXX: 由于调用外部邮件服务器发送邮件,因此耗时较长,请使用异步方法调用以防止UI卡死。

        public void SendEmail(string to, string subject, string body)

4.3.5    对于代码块总结性地注释

对于代码块的总结性注释可使读者在深刻细节以前就能获得该代码块的主旨,甚至有时候均可以直接跳过该代码块,从而能够快速准确的把握代码。

如读者看到://下面代码使用了二分查找算法来快速的根据用户Id找到相应用户

那么他就能够快速理解下面代码的逻辑,不然本身看二分查找仍是要用些时间的。

4.4    如何写好注释

4.4.1    避免使用不明确的代词

有些状况下,”it”, “this”等代词指代很容易产生歧义,最安全的方式是不要使用将全部可能产生歧义的代词替换成实际指代的词。

如://Insert the data into the cache,but check if it's too big first.

”it”是指”data“仍是”cache“? 在读完剩下的代码前谁也不知道指代的是谁。那还要注释作什么?替换成要指代的词后读者就能够直接了当的知道接下来的代码要作什么了。

//Insert the data into the cache,but check if the data is too big first.

4.4.2    精确描述方法的行为

注释必定要精确的描述方法的行为。避免因为注释不许确而形成的误调用。

如你写了一个方法统计文件中的行数

        //Return the number of lines in this file

        public long CountLinesInFile(string fileName)

上面的注释不是很精确,由于有不少定义行的方式,下面几种状况这个方法的返回值没法根据注释快速的判断出来。

  1. “”(空文件)——0或1行?
  2. “hello”­­——0或1行?
  3. “hello\n”­­——1或2行?
  4. “hello\n\r world\r”­­——二、3或4行?

假设该方法的实现是统计换行符的(\n)的个数,下面的注释就要比原来的注释更好些。

//Count how many newline symbols('\n') are this file

这条注释包含更多的信息。读者能够知道若是没有换行符,这个函数会返回0。读者还知道回车符(\r)会被忽略。

4.4.3    用输入输出例子来讲明特殊的状况

对于注释来说,一个精挑细选的例子比千言万语还要有效,并且更加直白有效,阅读速度更快。

如:        /// <summary>

        /// Remove the suffix/prefix of charsToRemove from the input source

        /// </summary>

        public string StripPrefixAndSuffix(string source, string charsToRemove)

这条注释不是很精确,由于它不能回答下面的问题

  1. 是只有按charsToRemove中顺序的字符才会被移除,仍是无序的charsToRemove也会被移除?
  2. 若是在开头和结尾有多个charsToRemove会怎样?

而一个好例子就能够简单直白的回答这些问题:

 /// <summary>

        /// Example: StripPrefixAndSuffix("abbayabbazbaba","ab") returns "yababz"

        /// </summary>

4.4.4    更新代码时记得更新注释

再好的注释也会随着内容的更改而变得愈来愈没有意义,有时候甚至会对读者形成误导,产生没必要要的bug。因此在更改代码后,记得要更新所更改代码的注释,使其表达最新代码的含意。

4.4.5    只有能让别人读懂的注释才是合格的注释

当本身不肯定本身的注释是否合格时,请周围的同事读下你的注释,看他读完注释后说出的想法是不是你想要表达的,是否有信息遗漏和误解等。

4.5    region的使用

以下图所示:

 

4.6    c#中巧用#if debug进行调试

#if DEBUG

    UserID = "abc@test.com";

    Password = "123456";

#endif

当调试代码的时候加上适当的判断,而不影响Release的代码。

经过#if预编译指令对DEBUG进行判断,以下:

#if DEBUG

  // 调试用代码

   ……#endif

调试用代码在Debug状态下是要执行的,而在Release状态下根本执行,在生成的时候也直接忽略。

4.7    c#特性代码简洁

  1.var car_Permission = _car_PermissionBusiness.GetModel(car_User.RoleID) ?? null;

 

  2.var car_Permission = _car_PermissionBusiness.GetModel(car_User.RoleID) == null ? new Car_Permission() : _car_PermissionBusiness.GetModel(car_User.RoleID);

 

  3.var userName= _car_UserBusiness.GetCarUser(car_User.RoleID)?.UserName ?? null;

总结:第一种跟第二种获取的数据方法是同样的

相关文章
相关标签/搜索