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;

總結:第一種跟第二種獲取的數據方法是同樣的

相關文章
相關標籤/搜索