動態

詳情 返回 返回

C# 的代碼樣式 - 動態 詳情

標識符命名規則與規範

標識符是您為類型(class、interface、struct、delegate 或 enum)、成員、變量或命名空間所賦予的名稱。

命名規則

有效的標識符必須遵循以下規則。如果任何標識符不符合這些規則,C# 編譯器將會產生錯誤:

  • 標識符必須以字母或下劃線(_)開頭。
  • 標識符可以包含 Unicode 字母字符、十進制數字字符、Unicode 連接字符、Unicode 結合字符或 Unicode 格式字符。

您可以使用 @ 前綴來聲明與 C# 關鍵字匹配的標識符。@ 不是標識符名稱的一部分。例如,@if 會聲明一個名為 if 的標識符。這些轉義標識符主要用於與其他語言聲明的標識符進行互操作。

重要事項:C# 語言規範僅允許以下類別:字母(Lu、Ll、Lt、Lm 或 Nl)、數字(Nd)、連接符(Pc)、組合符(Mn 或 Mc)以及格式符(Cf)。超出這些範圍的任何內容都會自動使用 “_” 進行替換。這可能會對某些 Unicode 字符產生影響。

命名規範

除了規則之外,標識符名稱的規範在 .NET API 中也得到了廣泛應用。這些規範為名稱提供了統一性,但編譯器不會強制執行這些規範。您可以在自己的項目中自由採用不同的規範。

按照慣例,C# 程序中類型名稱、命名空間以及所有公共成員均採用 PascalCase 格式。此外,dotnet/docs 團隊還採用了以下約定,這些約定借鑑了.NET 運行時團隊的編碼風格:

  • interface(接口)名稱以大寫字母 “I” 開頭。
  • property(屬性)類型均以 “Attribute” 這個詞結尾。
  • enum(枚舉)類型對於非標誌項使用單數名詞,而對於標誌項則使用複數名詞。
  • 標識符中不應包含連續的兩個下劃線字符(_)。此類名稱是專用於編譯器自動生成的標識符的。
  • 為變量、方法和類賦予有意義且描述性的名稱。
  • 寧要清晰明瞭,不要簡略粗略。
  • 對於類名和方法名,請使用 PascalCase 格式。
  • 對於方法參數和局部變量,請使用駝峯式命名法。
  • 對於常量名稱、字段名稱以及局部常量,均應使用 PascalCase 格式。
  • private 實例字段以一個下劃線(_)開頭,其餘部分則採用駝峯式命名法。
  • static 字段以 “s_” 開頭。這種約定並非是 Visual Studio 的默認行為,也不是框架設計指南中的內容,但可以在 editorconfig 中進行配置。
  • 在名稱中避免使用縮寫或縮略詞,除非是廣為人知且被認可的縮寫形式。
  • 使用有意義且具有描述性的命名空間,並遵循反向域名的命名規則。
  • 選擇能夠體現該程序集主要用途的名稱。
  • 避免使用單字母名稱,除非是簡單的循環計數器。此外,描述 C# 結構語法的示例通常會使用與 C# 語言規範中所採用的慣例相一致的以下單字母名稱。語法示例是對這一規則的例外情況。

    • 用 S 表示 struct(結構體),用 C 表示 class(類)。
    • 用 M 表示方法。
    • 用 v 表示變量,用 p 表示參數。
    • 用 r 表示引用參數。

提示:您可以通過使用代碼風格命名規則來強制執行與大小寫、前綴、後綴以及單詞分隔符相關的命名規範。

在以下示例中,針對標有 “public” 標識的元素所提供的指導同樣適用於處理 “protected” 和 “protected internal” 元素的情況,所有這些元素均旨在對外部調用者可見。

帕斯卡命名法

在為 class、interface、struct 或 delegate 類型命名時,請使用帕斯卡命名法(“PascalCasing”)。

public class DataService
    {
    }

public record PhysicalAddress (
    string Street,
    string City,
    string StateOrProvince,
    string ZipCode );

public struct ValueCoordinate
    {
    }

public delegate void DelegateType ( string message );

在為接口命名時,除了在名稱前加上 “I” 這個前綴外,還應採用帕斯卡命名法。這個前綴能清晰地向使用者表明這是一個接口。

public interface IWorkerQueue
    {
    }

在為類型中的 public 成員(如字段、屬性、事件)命名時,請使用帕斯卡命名法。此外,對於所有方法和局部函數也應使用帕斯卡命名法。

public class ExampleEvents
    {
        // public 字段,應儘量少用
        public bool IsValid;

        // 一個僅用於初始化的屬性
        public IWorkerQueue WorkerQueue { get; init; }

        // 一個事件
        public event Action EventProcessing;

        // 方法
        public void StartEventProcessing ( )
            {
                // 本地函數
                static int CountQueueItems ( ) => WorkerQueue . Count;
                // ……
            }
    }

在編寫位置 record 時,對於參數應使用帕斯卡命名法,因為它們是 record 的 public 屬性。

駝峯式命名法

在為 private 或 internal 字段命名時,請使用駝峯式命名法(即“camelCasing”),並在其前加上“_”。在為局部變量(包括 delegate 類型的實例)命名時也應使用駝峯式命名法。

public class DataService{
private IWorkerQueue _workerQueue;}

提示:在支持語句補全的集成開發環境中編輯遵循這些命名規則的 C# 代碼時,輸入 “_” 後將會顯示所有對象作用域內的成員。

在處理 private 或 internal 的 static 字段時,請使用 “s_” 前綴;而處理線程 static 字段時,則使用 “t_” 前綴。

public class DataService{
private static IWorkerQueue s_workerQueue;
[ThreadStatic]
private static TimeSpan t_timeSpan;}

在編寫方法參數時,請使用駝峯式命名法。

public T SomeMethod < T > ( int someNumber , bool isValid ){ }

類型參數命名準則

以下準則適用於泛型類型參數中的類型參數。類型參數是在泛型類型或泛型方法中充當參數佔位符的元素。

  • 對於通用類型參數,應使用具有描述性名稱的名稱來表示,除非單個字母的名稱本身就完全清晰易懂,而使用描述性名稱並不會帶來任何額外價值的話。

    public interface ISessionChannel < TSession > { /*……*/ }
    public delegate TOutput Converter < TInput , TOutput >( TInput from );
    public class List < T > { /*……*/ }
  • 考慮將 T 用作具有單個字母類型參數的類型的類型參數名稱。

    public int IComparer < T > ( ) => 0;
    public delegate bool Predicate < T > ( T item );
    public struct Nullable < T > where T : struct { /*……*/ }
  • 在描述性類型參數名稱前加上 “T” 作為前綴。

    public interface ISessionChannel < TSession >
      {
          TSession Session { get; }
      }
  • 可以考慮在參數名稱中明確表示對類型參數所施加的限制。例如,被限制為 “ISession” 的參數可以命名為 “TSession”。

代碼分析規則 CA1715 可用於確保類型參數的命名恰當。

額外的命名規範

不包含使用指令的示例中,應使用命名空間限定符。如果您知道某個命名空間在項目中默認已導入,則無需對來自該命名空間的名稱進行完整限定。如果限定名稱過長而無法在一行內顯示,則可以在句點(.)之後對其進行拆分,如下例所示。

var currentPerformanceCounterCategory = new System . Diagnostics .  PerformanceCounterCategory ( );

您無需更改是通過使用 Visual Studio 設計工具創建的對象的名稱,以使其符合其他規範要求。

常見的 C# 代碼規範

編碼規範對於保持代碼的可讀性、一致性以及團隊內部的協作至關重要。遵循行業慣例和既定準則編寫的代碼更易於理解、維護和擴展。大多數項目都會通過編碼規範來保持統一的風格。dotnet/docs 和 dotnet/samples 這兩個項目也不例外。在這一系列的文章中,您將瞭解到我們的編碼規範以及我們用於執行這些規範的工具。您可以直接採用我們的規範,或者根據您團隊的需求對其進行修改。
我們制定這些慣例是基於以下目標:

  1. 正確性:我們將樣本複製粘貼到您的應用程序中。我們期望如此,因此我們需要編寫具有彈性和正確性的代碼,即便經過多次修改也是如此。
  2. 教學:我們的樣本旨在教授整個 .NET 和 C# 知識體系。因此,我們不對任何語言特性或 API 設置限制。相反,這些樣本會教導用户何時選擇某個特性是明智之舉。
  3. 一致性:讀者期望我們在所有內容中獲得一致的體驗。所有樣本都應遵循相同的風格。
  4. 採用:我們積極更新樣本以使用新的語言特性。這種做法提高了對新特性的認識,並使所有 C# 開發者都能更熟悉這些特性。

重要事項:這些指南由 Microsoft 用於開發示例和文檔。它們源自 .NET CLR、C# 編碼風格和 C# 編譯器(Roslyn)的指南。我們選擇這些指南是因為它們在多年的開源開發過程中得到了採用。這些指南有助於社區成員參與運行時和編譯器項目。它們旨在成為常見的 C# 共同規範的示例,並非權威清單。
教學和採用的目標是導致文檔中的編碼規範與運行時和編譯器規範有所不同的原因。運行時和編譯器對熱點路徑都有嚴格的性能指標要求。而其他許多應用程序則沒有這樣的要求。我們的教學目標規定,我們不能禁止任何特定的結構。相反,示例會説明何時應該使用這些結構。我們更新示例的頻率比大多數生產應用程序都要高。我們的採用目標規定,我們必須展示今天應該編寫的代碼,即使去年編寫的代碼不需要修改。

本文闡述了我們的指導原則。這些指導原則會隨着時間的推移而不斷更新,您會發現有些示例並不符合我們的原則。我們歡迎能夠使這些示例符合規範的 PRs,或者能夠引起我們對需要更新的示例予以關注的問題。我們的指導原則是開源的,我們歡迎 PRs 和問題。然而,如果您提交的內容會改變這些建議,請先提出問題進行討論。您可以自由使用我們的指導原則,或者根據您的需求對其進行調整。

工具與分析器

工具能夠幫助您的團隊執行您的規範。您可以啓用代碼分析來執行您所偏好的規則。您還可以創建一個 editorconfig 文件,以便 Visual Studio 自動執行您的樣式指南。作為起點,您可以複製 dotnet/docs.editorconfig 文件以採用我們的樣式。

這些工具能讓您的團隊更輕鬆地遵循您所選定的規範。Visual Studio 會將所有適用的 .editorconfig 文件中的規則應用到您的代碼中,以對其進行格式化。您可以使用多種配置來執行公司範圍的規範、團隊規範,甚至精細到項目的規範。

代碼分析在檢測到違反規則的情況時會發出警告和診斷信息。您可配置要應用於您的項目的規則。然後,每次持續集成構建都會通知開發人員,告知他們是否違反了任何規則。

診斷標識

在構建自己的分析器時,請選擇合適的診斷標識。

語言指南

以下各部分介紹了.NET 文檔團隊在準備代碼示例和樣本時所遵循的規範。總體而言,請遵循以下這些做法:

  • 儘可能利用現代語言特性和 C# 版本。
  • 避免使用過時的語言結構。
  • 只捕獲可以妥善處理的異常;避免捕獲一般異常。例如,示例代碼不應在沒有異常過濾器的情況下捕獲 System . Exception 類型。
  • 使用具體的異常類型來提供有意義的錯誤消息。
  • 使用 LINQ 查詢和方法進行集合操作以提高代碼可讀性。
  • 使用 async 和 await 進行異步編程來處理 I/O 密集型操作。
  • 注意死鎖問題,並在適當情況下使用 Task . ConfigureAwait。
  • 使用語言關鍵字來表示數據類型,而不是運行時類型。例如,使用 string 而不是 System . String,或者使用 int 而不是 System . Int32。此建議還包括使用 nint 和 nuint 類型。
  • 使用 int 而不是無符號類型。在 C# 中,使用 int 是常見的做法,並且使用 int 時與其他庫的交互會更容易。異常是為特定於無符號數據類型的文檔而設計的。
  • 僅在讀者可以從表達式中推斷出類型時使用 var。讀者在 docs 平台上查看我們的示例。他們沒有懸停或工具提示來顯示變量的類型。在編寫代碼時要注重清晰性和簡潔性。
  • 避免使用過於複雜和繁瑣的代碼邏輯。

以下是一些更具體的指導原則。

字符串(string)數據

  • 使用 string 插值來連接短字符串,如以下代碼所示。
    string XM = $"{lbXM [ n ] . 姓}{lbXM [ n ] . 名}"
  • 在循環中添加字符串時(尤其是當您處理大量文本時),請使用 System . Text . StringBuilder 對象。

    var Li = "離離離離離離離";
    var Li許多 = new StringBuilder ( );
    for ( var i = 0 ; i < 100 ; i++ )
      {
          Li許多 . Append ( Li );
      }
    Console . WriteLine ( "離了嗎:" + Li許多 );
  • 優先使用原始字符串字面值,而非轉義序列或轉義字符串。

    var message = """
        這是一條跨越多行的長消息。
        它使用了原始字符串字面值。這意味着我們可以
        直接包含像 \n 和 \t 這樣的字符,而無需對其進行轉義。
        """;
  • 請使用基於表達式的字符串插值方式,而非基於位置的字符串插值方式。

    // 執行查詢
    Console . WriteLine ( "成績查詢:" );
    foreach ( var XS in 成績查詢 )
      {
          Console . WriteLine ( $"{XS . Last} 成績:{XS . 成績}" );
      }

構造函數與初始化

  • 對於 record 類型的主構造函數參數,應採用 Pascal 框架的命名方式:
    public record Ren ( string 姓 , string 名 );
  • 對於 class 和 struct 類型的主構造函數參數,應使用駝峯式命名法。
  • 使用必需的屬性而非構造函數來強制初始化屬性值:

    public class LabelledContainer < T > ( string label )
      {
          public string Label { get; } = label;
          public required T Contents
              {
                  get;
                  init;
              }
      }

數組與集合

  • 使用集合表達式初始化集合類型:
    string [ ] YY = [ "a" , "e" , "i" , "o" , "u" ];

委託

  • 請使用 Func < > 和 Action < > 而不是定義 delegate 類型。在 class 中,定義 delegate 方法即可。

    Action < string > actionExample1 = x => Console . WriteLine ( $"x 是:{x}" );
    
    Action < string , string > actionExample2 = ( x , y ) => Console . WriteLine ( $"x 是:{x} , y 是:{y}" );
    
    Func < string , int > funcExample1 = x => Convert . ToInt32 ( x );
    
    Func < int , int , int > funcExample2 = ( x , y ) => x + y;
  • 使用由 Func < > 或 Action < > 委託定義的簽名來調用該方法。

    actionExample1 ( "string for x" );
    
    actionExample2 ( "string for x", "string for y" );
    
    Console . WriteLine ( $"The value is {funcExample1 ( "1" )}" );
    
    Console . WriteLine ( $"The sum is {funcExample2 ( 1 , 2 )}" );
  • 如果您要創建委託類型的實例,請使用簡潔的語法。在 class 中,先定義委託類型,然後定義一個具有相應簽名的方法。

    public delegate void Del ( string 信息 );
    
    public static void FFDel ( string 字符串 )
      {
          Console . WriteLine ( $"FFDel 參數:{字符串}" );
      }
  • 創建該委託類型的實例並調用它。以下的聲明展示了簡化的語法。

    Del exampleDel2 = FFDel;
    exampleDel2 ( "Hey" );
  • 以下聲明採用了完整的語法結構。

    Del exampleDel1 = new ( FFDel );
    exampleDel1 ( "Hey" );

在異常處理中使用 try-catch 語句和 using 語句

  • 對於大多數異常處理,應使用 try-catch 語句。

    static double FF點距 ( double x1 , double y1 , double x2 , double y2 )
      {
          try
              {
                  return Math . Sqrt ( Math . Pow ( ( x1 - x2 ) , 2 ) + Math . Pow ( (y1 - y2) , 2 ) );
              }
          catch ( System . ArithmeticException yc )
              {
                  Console . WriteLine ( $"算術上溢或下溢:{yc}" );
                  throw;
              }
      }
  • 通過使用 C# 的 using 語句來簡化您的代碼。如果您有一個 try……finally 語句,其中 finally 塊中的唯一代碼是調用 Dispose 方法,那麼請改用 using 語句。
    在以下示例中,try……finally 語句僅在 finally 塊中調用 Dispose 方法。

    Font ti樣式 = new Font ( "Arial" , 10.0f );
    try
      {
          byte charset = ti樣式 . GdiCharSet;
      }
    finally
      {
          ti樣式? . Dispose ( );
      }

    使用 using 語句獲得同樣作用:

    using ( Font arial = new Font ( "Arial", 10.0f ) )
      {
          byte charset2 = arial . GdiCharSet;
      }

    使用新的 “using” 語法,它無需使用花括號:

    using Font YS正常 = new Font ( "Arial" , 10.0f );
    byte charset3 = YS正常 . GdiCharSet;

&& 運算符和 || 運算符

  • 在進行比較操作時,請使用 && 替代 & ,使用 || 替代 | ,如以下示例所示。

    Console . Write ( "輸入被除數:" );
    int Z被除數 = Convert . ToInt32 ( Console . ReadLine ( ) );
    
    Console . Write ( "輸入除數:" );
    int Z除數 = Convert . ToInt32 ( Console . ReadLine ( ) );
    
    if ( ( Z除數 != 0 ) && ( Z被除數 / Z除數 ) is var Z結果 )
      {
          Console . WriteLine ( $"商:{Z結果}" );
      }
    else
      {
          Console . WriteLine ( "由於試圖對零進行除法運算,結果就出現了這種情況。" );
      }

    如果除數為 0,if 語句中的第二條語句就會導致運行時錯誤。但 && 運算符在第一個表達式為 false 時會中斷運算過程。也就是説,它不會計算第二個表達式。& 運算符則會同時計算兩個表達式,當除數為 0 時,就會導致運行時錯誤。

new 運算符

  • 當變量類型與對象類型相匹配時,可以使用對象實例化的簡潔形式,如以下聲明所示。當變量是 interface 類型或 RunTime 類型的基類時,此形式無效。

    var Example1 = new LEIExample ( );
    LEIExample ShiLi1 = new ( );

    上述聲明與以下聲明是等同的。
    LEIExample Example2 = new LEIExample ( );

  • 使用對象初始化器可以簡化對象的創建過程,如下面的示例所示。
    var Example3 = new LEIExample { 名稱 = "桌面" , ID = 37414 , 位置 = "辦公室" , 年齡 = 2.3 };
    以下示例設置了與前面示例相同的屬性,但未使用初始化器。

    var Example4 = new LEIExample ( );
    Example4 . 名稱 = "桌面";
    Example4 . ID = 37414;
    Example4 . 位置 = "家";
    Example4 . 年齡 = 6.7;

事件(event)處理

  • 使用 lambda 表達式定義一個事件處理程序,您無需在以後將其移除:

    public CHT主 ( )
      {
          this . Click += ( s , e ) =>
              {
                  MessageBox . Show ( ( ( MouseEventArgs ) e ) . Location . ToString ( ) );
              };
      }

    lambda 表達式縮短了以下傳統定義。

    public CHT主 ( )
      {
          this . Click += new EventHandler ( CHT主_Click );
      }
    
    void CHT主_Click ( object? sender , EventArgs e )
      {
          MessageBox . Show ( ( ( MouseEventArgs ) e ) . Location . ToString ( ) );
      }

static(靜態)成員

通過使用類名來調用 static 成員:類名 . Static成員。這種做法有助於使代碼更具可讀性,因為它能清晰地表明 static 訪問方式。不要在基類中定義的 static 成員前加上派生類的名稱進行限定。雖然這樣的代碼可以編譯通過,但其可讀性會變得不明確,而且如果將來你在派生類中添加具有相同名稱的 static 成員,那麼這段代碼可能會出現錯誤。

LINQ 查詢

  • 請為查詢變量賦予有意義的名稱。以下示例中,使用 “KH青島” 來表示位於青島的客户。

    var KH青島 = from KH in KHs
               where KH . 城市 == "青島市"
               select KH . 名稱;
  • 使用別名可以確保匿名類型的屬性名稱以大寫形式正確呈現,採用的是帕斯卡命名法(PascalCasing)。

    var JXS本地 = from KH in KHs
                join JXS in JXSs on KH . 城市 equals JXS . 城市
                select new { 客户 = KH , 經銷商 = JXS };
  • 當查詢結果中的屬性名稱可能產生歧義時,應重新命名這些屬性。例如,如果您的查詢返回客户名稱和經銷商名稱,而不將它們以 “名稱” 的形式出現在結果中,那麼應將其重命名為以明確表示 “客户名稱” 代表的是客户的名稱,而 “經銷商名稱” 代表的是經銷商的名稱。

    var JXS本地 =
       from KH in KHs
       join JXS in JXSs on KH . 城市 equals JXS . 城市
       select new { 客户名稱 = KH . 名稱 , 經銷商名稱 = JXS . 名稱 };
  • 在查詢變量和範圍變量的聲明中使用隱式類型。關於 LINQ 查詢中的隱式類型這一指導原則會取代一般對於隱式類型局部變量的規則。LINQ 查詢經常使用投影來創建匿名類型。其他查詢表達式則會使用嵌套的泛型類型來生成結果。隱式類型變量通常更具可讀性。

    var KH呼和浩特 = from KH in KHs
                  where KH . 城市 == "呼和浩特市"
                  select KH . 名稱;
  • 將查詢子句置於 “from” 子句之下,如前面的示例所示。
  • 在其他查詢語句之前使用 “where” 子句,以確保後續的查詢語句僅對經過縮減和篩選後的數據集進行操作。

    var KH鄭州 = from KH in KHs
               where KH . 城市 == "鄭州市"
               orderby KH . 名稱
               select KH;
  • 通過多個 “from” 子句來訪問內部集合,而非使用 “join” 子句。例如,一個包含學生對象的集合中,每個學生對象可能都包含一組考試成績。當執行以下查詢時,它會返回所有超過 90 分的成績,以及獲得該成績的學生的姓氏。

    var 分數查詢 = from 學生 in 學生s
                          from 分數 in 學生 . 分數
                          where 分數 > 90
                          select new { 姓 = 學生 . 姓 , 分數 };

隱式類型局部變量

  • 當變量的類型從賦值右側的內容中就能明顯看出時,應使用隱式類型來定義局部變量。

    var Z信息 = "這真是一個字符串!";
    var V温度 = 27;
  • 當賦值右側的類型無法明確判斷時,不要使用 “var” 關鍵字。不要假定從方法名就能明確得知變量的類型。如果變量類型是 new 運算符、顯式類型轉換或者賦值給常量值,那麼就可以認為其類型是明確的。

    int Z迭代數 = Convert . ToInt32 ( Console . ReadLine ( ) );
    int Z當前最大數 = LEIExample . 最大結果 ( );
  • 不要使用變量名來指定變量的類型。這樣做可能並不準確。相反,應使用類型來指定變量的類型,而使用變量名來表明變量的語義信息。以下示例應將類型設為 “string”,並使用類似 “iterations(迭代次數)” 這樣的名稱來表示從控制枱讀取的信息的含義。

    var inputInt = Console . ReadLine ( );
    Console . WriteLine ( inputInt );
  • 避免使用 “var” 來替代“dynamic”。當您希望進行運行時類型推斷時,請使用 “dynamic”。
  • 在 for 循環中,對循環變量使用隱式類型定義。
    以下示例在 for 語句中使用了隱式類型定義。

    var Li = "離離離離離離離離離離離離離離離離離離離離";
    var Li很多 = new StringBuilder ( );
    for ( var i = 0 ; i < 10000 ; i++ )
      {
          Li很多 . Append ( Li );
      }
  • 在 foreach 循環中,不要使用隱式類型來確定循環變量的類型。在大多數情況下,集合中元素的類型並不一目瞭然。不應僅僅依靠集合的名稱來推斷其元素的類型。
    以下示例在 “foreach” 語句中使用了顯式類型定義。

    foreach ( char Z in ZFCs )
      {
          if ( Z == 'h' )
              {
                  Console . Write ( "H" );
              }
          else
              {
                  Console . Write ( Z );
              }
      }
    Console . WriteLine ( );
  • 在 LINQ 查詢中,應使用隱式類型來表示結果序列。關於 LINQ 的部分解釋説,許多 LINQ 查詢會生成匿名類型,此時必須使用隱式類型。而其他查詢會生成嵌套的泛型類型,此時使用 var 則更具可讀性。

注意:請注意,切勿無意中更改可迭代集合中某個元素的類型。例如,在 foreach 語句中很容易將 System . Linq . IQueryable 類型轉換為 System . Collections . IEnumerable 類型,這會改變查詢的執行方式。

我們的部分示例能夠明確表達出一種表達式的自然類型。這些示例必須使用 “var” 關鍵字,以便編譯器能夠識別出其自然類型。儘管這些示例的解釋性稍弱,但使用 “var” 是該示例所必需的。文章應當對這種行為進行説明。

文件範圍內的命名空間聲明

大多數代碼文件都會聲明一個單一的命名空間。因此,在我們的示例中應採用文件範圍內的命名空間聲明方式:
namespace MySampleCode;

將 “using” 指令置於命名空間聲明之外

當 “using” 指令位於命名空間聲明之外時,所導入的命名空間就是其完整的限定名稱。這種完整的限定名稱更為清晰。而當 “using” 指令位於命名空間內部時,它可以相對於該命名空間,也可以是其完整的限定名稱。

using Azure;

namespace CoolStuff . AwesomeFeature
    {
        public class Awesome
            {
                public void Stuff ( )
                    {
                        WaitUntil wait = WaitUntil . Completed;
                        // ……
                    }
            }
    }

假設存在對 “WaitUntil” 類的引用(直接引用(direct)或間接引用(indirect))。

現在,讓我們稍作改動:

namespace CoolStuff . AwesomeFeature
    {
        using Azure;
        public class Awesome
            {
                public void Stuff ( )
                    {
                        WaitUntil wait = WaitUntil . Completed;
                        // ……
                    }
            }
    }

它今天能編譯通過。明天也能編譯通過。但隨後在接下來的某一週內,之前未修改的代碼會出現兩個錯誤而無法正常運行了:

- 警告 錯誤 CS0246:無法找到 ‘WaitUntil’ 這個類型或命名空間(您可能缺少了 using 指令或未引用相應的程序集?)
- 警告 錯誤 CS0103:在當前上下文中不存在 ‘WaitUntil’ 這個名稱

該類是在某個命名空間中引入的,而這個命名空間的名稱以 “.Azure” 結尾:

namespace CoolStuff.Azure
    {
        public class SecretsManagement
            {
                public string FetchFromKeyVault ( string vaultId , string secretId ) { return null; }
            }
    }

嵌入到命名空間內的使用指令具有上下文敏感性,會增加名稱解析的複雜性。在該示例中,它會查找第一個遇到的命名空間。

  • CoolStuff . AwesomeFeature . Azure
  • CoolStuff . Azure
  • Azure

添加一個與 “CoolStuff . Azure” 或 “CoolStuff . AwesomeFeature . Azure” 相匹配的新命名空間,其位置會先於全局的 “Azure” 命名空間。您可以通過在使用聲明中添加 “global::” 修飾符來解決此問題。不過,將使用聲明放在命名空間之外會更簡單一些。

namespace CoolStuff . AwesomeFeature
    {
        using global::Azure;

        public class Awesome
            {
                public void Stuff ( )
                    {
                        WaitUntil wait = WaitUntil . Completed;
                        // ……
                    }
            }
    }

樣式指南

通常,代碼示例應採用以下格式:

  • 使用四個空格進行縮進。不要使用製表符。
  • 保持代碼對齊一致以提高可讀性。
  • 將每行限制在 65 個字符以內,以增強文檔中的代碼可讀性,尤其是在移動設備屏幕上。
  • 通過將長語句拆分為多行來提高清晰度和用户體驗。
  • 使用 “阿曼” 風格的大括號:大括號各自佔新行。大括號與當前縮進級別對齊。
  • 如有必要,應在二元運算符之前換行。

註釋風格

  • 使用單行註釋(//)進行簡短説明。
  • 避免使用多行註釋(/ /)來進行較長的解釋。
  • 代碼示例中的註釋未進行本地化。這意味着嵌入在代碼中的解釋不會被翻譯。較長的解釋性文本應放在配套的文章中,以便進行本地化。
  • 對於描述方法、類、字段以及所有公共成員,請使用 XML 註釋。
  • 請將註釋放在單獨的一行,不要放在代碼行的末尾。
  • 請以大寫字母開始評論文本。
  • 以句號結束評論文本。
  • 在註釋分隔符(//)和註釋文本之間插入一個空格,如下例所示。

    // 以下聲明創建了一個查詢。
    // 但它不會運行該查詢。

    佈局約定

    良好的佈局會使用格式設置來突出代碼的結構,並使代碼更易於閲讀。微軟的示例和樣本遵循以下約定:

  • 使用默認的代碼編輯器設置(智能縮進、四個空格的縮進、製表符保存為空格)。
  • 每行只寫一個陳述。
  • 每行只寫一個聲明。
  • 如果續行沒有自動縮進,請將其縮進一個製表位(四個空格)。
  • 在方法定義和屬性定義之間至少添加一個空行。
  • 使用括號使表達式中的從句清晰明瞭,如以下代碼所示。

    如果( x起始座標 > x結束座標 )&& ( x起始座標 > x前一個座標 )
      {
          // 採取適當的措施……
      }

    例外情況是當樣本能夠解釋運算符或表達式的優先級時。

Add a new 評論

Some HTML is okay.