命名空間的作用

命名空間的核心價值很簡單:幫型別分組,降低命名衝突,讓程式碼更容易定位。

例如:

using System.IO;
using System.Text;

這兩行不是把型別「載入」進來,而是告訴編譯器:之後在解析型別名稱時,可以優先到這些命名空間裡找。

編譯器如何解析型別名稱

假設程式碼裡寫了:

FileStream stream;
StringBuilder builder;

編譯器會嘗試根據目前作用域、using 指示詞,以及完整限定名稱來解析 FileStreamStringBuilder

如果前面有:

using System.IO;
using System.Text;

那它就能分別找到:

  • System.IO.FileStream
  • System.Text.StringBuilder

命名空間的功能,本質上就是幫助名稱解析,而不是改變型別本身。

名稱衝突與完整限定名

如果兩個命名空間內剛好都有同名型別,就需要明確指定:

A.Hello first = new A.Hello();
B.Hello second = new B.Hello();

只寫 Hello 時,編譯器無法判斷你要哪一個型別;寫成完整限定名後,歧義就消失了。

這也是為什麼大型專案裡,命名空間規劃不能太隨意。只靠「型別名稱取好一點」通常不夠。

別名

如果完整限定名太長,或者衝突太頻繁,可以給命名空間或型別起別名:

using WinForms = System.Windows.Forms;

之後就能這樣寫:

WinForms.Form form = new WinForms.Form();

global:: 也很常用,它表示從全域命名空間根節點開始解析:

global::Configuration config = new global::Configuration();

當專案內部又宣告了同名命名空間或型別時,這種寫法特別有用。

另外還有 extern alias,用來處理不同程序集之間的同名型別衝突:

extern alias TestAlias;

這通常只會在比較複雜的程序集引用場景出現,但知道它的存在很重要。

命名空間與程序集不是一回事

初學時很容易把命名空間和程序集混在一起,但它們其實是兩個概念:

  • 命名空間:邏輯分組
  • 程序集:編譯與部署單位

它們不是一對一關係:

  • 一個程序集可以包含很多命名空間
  • 同一個命名空間也可以散落在多個程序集裡

所以看到 MyCompany.Tools.IO 這樣的命名空間名稱,不代表它一定只存在於某一個 DLL 裡。

小結

命名空間主要解決的是名稱管理問題,而不是執行期問題。整理起來有三個重點:

  • using 影響的是名稱解析,不是型別載入
  • 發生命名衝突時,可以改用完整限定名或別名
  • 命名空間和程序集彼此相關,但不是同一件事

把這個層次分清楚,之後讀大型程式碼庫會輕鬆很多。