C#筆記 – 分部類與分部方法

分部類
  • partial關鍵字

    • 用於將類型源代碼分散到多個文件

  • 實際上是在編譯之前在所有源文件合并在了一起,在一個文件中的代碼可以調用另外一個文件中的代碼

    • 每個獨立的成員必須完整地位於它所處的文件中

    • 聲明必須相互兼容

    • 任何文件都能指定要實現的接口(不一定在聲明的文件處實現)、基類、類型參數約束

    • 如果多個文件都設定了基類,那它們必須是相同的(可以一個聲明,一個不聲明,但聲明了就必須相同),類型約束也是一樣。

    • 兩個類型必須具有相同的訪問修飾符

  • 在多個文件的情況下,成員和靜態變量的初始化不一定是按照順序發生的

  • 用途:

    • 連接設計器和其他代碼生成器。行用分部類型模型,代碼生成器可以擁有自由操作的文件,或者重寫整個文件

      • 避免自動生成的代碼與人手編寫的代碼發生衝突
    • 添加行為到類型變得非常簡單:重寫虛方法、添加帶有業務邏輯的新成員等等

      • 方便讓不同程序員同時對一個類進行編輯,把類分成不同的邏輯單元

    • 輔助重構

    • 單元測試

分部方法
  • 使用時機:使用了工具生成了C#源代碼文件,又想對裡面的行為進行定制

    • 如果不使用分部方法,而是使用繼承的話

      • internal class Base
        {
           string m_Name;
           protected virtual void OnNameChanging(string value) { }

           public string Name
          {
               get { return m_Name; }
               set
              {
                   OnNameChanging(value.ToUpper());
                   m_Name = value;
              }
          }
        }

        internal class Derived : Base
        {
           protected override void OnNameChanging(string value)
          {
               if (string.IsNullOrEmpty(value)) { return; }
          }
        }
      • 工具生成的類型必須是非密封的,所以也不能用於值類型(無法繼承)

      • 也不能用於靜態方法,因為靜態方法不能重寫

      • 在效率上,也會浪費資源

        • 即使不去重寫定制的行為,但該行為仍然會被執行,也需要生成對應的IL代碼

    • 使用分部方法

      • internal sealed partial class Base
        {
           string m_Name;
           partial void OnNameChanging(string value);

           public string Name
          {
               get { return m_Name; }
               set
              {
                   OnNameChanging(value.ToUpper());
                   m_Name = value;
              }
          }
        }

        internal sealed partial class Base
        {
           partial void OnNameChanging(string value)
          {
               if (string.IsNullOrEmpty(value)) { return; }
          }
        }
      • 生成的類可以是靜態的,也可以是值類型

      • 工具生成和開發者的代碼不是兩個獨立的類,也沒有繼承關係,就是一個類型定義的兩個部分

      • 分部方法的聲明

        • 工具

          • 包含用partial關鍵字標記的聲明

          • 無主體

        • 開發者

          • 包含用partial關鍵字標記的聲明

          • 有主體

      • 如果分部方法沒有被實現,編譯器就不會生成任何調用分部方法的IL指令以及元數據

      • 使用規則

        • 只能在分部類/結構中聲明

        • 由於方法在運行時可能實際上並不存在(沒有被實現),所以分部方法的返回類型必須為void,參數也不能用out標記

          • 可以有ref參數、泛型方法、實例/靜態方法、unsafe標記

        • 分部方法的聲明和實現需具有完全一致的簽名,兩個方法和其中參數的特性都會被合并

        • 如果沒有對應的實現部分,就不能讓委托引用該分部方法

        • 總是為private

參考書目

  • 《CLR via C#》(第4版) Jeffrey Richter
  • 《深入理解C#》(第3版) Jon Skeet