命名空間本質上就是一個邏輯分組,就和我們文件夾分組是一致的,允許我們將相關的類型集合在一起。命名空間是一個比較通用的概念,在很多語言里面都存在,只不過可能存在一些細微差別以及使用不同的名稱。比如Java就叫包(package)。
接下來,我們將從以下五個方面來講解命名空間:
定義命名空間使用命名空間正確理解命名空間含義使用命名空間alias解決簡單類名沖突使用extern alias解決Assembly中的完整類名沖突定義命名空間
命名空間的定義很簡單,是以關鍵字namespace(命名空間的英文名)開始,后跟命名空間的名字和一對大括號。大括號里的內容都屬於這個命名空間。格式如下:
namespace namespace_name {
}
namespace_name是以點號(.)作為層級隔離的。比如System.IO就可以認為System是第一層命名空間,IO是第二層命名空間。
由於命名空間其實就是一個邏輯分組,所以namespace這個關鍵字,我們可以理解為“如果不存在namespace_name這個命名空間,就創建一個。如果存在,就使用這個命名空間”。因此,我們是可以在多個地方定義同一個命名空間的,只要保證同一個命名空間中的內容不沖突即可,如下:
namespace System.IO {
class MemoryStream {}
}
namespace System.IO {
class BufferedStream {}
}使用命名空間
在程序中使用特定類型的時候,我們需要使用完整的限定名,比如System.IO.MemoryStream。但是如果每次都這么寫,除了增加了開發人員打字的成本,毫無益處。因此我們可以使用using關鍵字來表明我們需要使用某個命名空間中的類型。如下:
//使用完整限定名
public static void Main() {
System.Text.StringBuilder sb=new System.Text.StringBuilder();
}
//使用using 關鍵字
using System.Text;
public static void Main() {
StringBuilder sb=new StringBuilder();
}
using關鍵字,其實是告訴編譯器可以嘗試在這個特定的命名空間中查找類型。
編譯器遇到一個類型的時候,按照如下順序查找:
查看當前源文件中是否有定義此類型在當前定義的命名空間中查找是否有定義此類型按定義順序在using語句中的命名空間中進行查找如果沒有找到或者找到多個,拋出編譯異常正確理解命名空間含義
我們在前面說到,命名空間是有層級關系的,通過點號(.)來分割層級。我們可以將命名空間的層級和嵌套類型聯系起來。本質上嵌套類型也是達到了一種層級關系。和嵌套類型一樣,命名空間的內部層級可以直接訪問到外部層級的類型和資源。如下面的例子,定義1和定義2 是等價的:
//定義1
namespace Outer.Middle.Inner {
public class Class1 {
private Class2 clazz2=new Class2(); //不會有編譯錯誤,無需聲明using Outer.Middle; 或者Outer.Middle.Class2
}
}
namespace Outer.Middle {
public class Class2 {}
}
//定義2
namespace Outer {
namespace Middle {
public class Class2 {}
namespace Inner {
public class Class1{
private Class2 clazz2=new Class2();//定義在內層命名空間的類型可以訪問定義在外層的類型
}
}
}
}
在上面的例子中,Class1可以直接訪問到定義在Outer或者Outer.Middle命名空間中的類型而無需使用using語句進行引用。
注意:需要注意的是,雖然內層命名空間可以直接訪問外層的類型,但是當有兩個同樣名字的類型分別定義在不同的層級,默認使用的是當前層級的類型。比如上面的例子在 Middle中也定義了一個Class1,那么在Inner中使用Class1默認是Outer.Middle.Inner.Class1,如果需要使用Middle的Class1就需要使用全限定名Outer.Middle.Class1。
使用命名空間alias解決簡單類名沖突
當出現像上文提到的有兩個Class1的情景時,使用全限定類名就變得不可避免。但是使用全限定名是一個很麻煩的事情,不光需要多打一些字,閱讀代碼也比較痛苦。為此,C#編譯器允許我們使用別名alias來指代命名空間,原本的全限定名就可以使用“別名.類名"的形式來替代了。如下面的例子:
namespace A.B.C {
using Middle=Outer.Middle;
using Outer.Middle.Inner;
public class ClassTest {
public Class1 innerClazz1=new Class1();
public Middle.Class1 middleClazz1=new Middle.Class1();
}
}
在上面的例子中,使用了前文定義的兩個Class1。如果我們像平常一樣直接引用兩個Class1的命名空間,直接使用Class1就會產生沖突,因為C#編譯器不能很好的理解我們需要的到底是是哪一個Class1。因此,我們給其中一個命名空間使用了別名Middle,當引用這個命名空間中的Class1時,我們就帶上別名。這樣編譯器就能夠正確識別類型了。
注意:CLR本身並不知道這個alias的存在,CLR使用的永遠是全限定類名。alias是C#編譯器提供的便利,在編譯階段依然會將其替換成全限定名。
使用extern alias解決Assembly中的完整類名沖突
在上一節,我們講到的是兩個類名沖突但是全限定名不沖突的類型。這種情況我們可以通過alias來規避。但是如果兩個類型的全限定名也完全一樣,那又該怎么處理呢?
很多同學肯定很疑惑為什么會出現這種情況。這種情況確實應該盡力避免,所以提倡使用公司的域名作為命名空間的一部分。但是有時候難免兩家公司提供的類型確實存在這種沖突,或者項目需要使用同一個Assembly的兩個不同版本。
這時,我們就需要使用到extern alias了。這也是一種別名,只不過這一次的別名是給Assembly的而不是命名空間。
使用extern alias,我們需要:
給某個Assembly一個alias在使用的類型中聲明 extern alias <別名>使用類型的時候,使用 <別名>::全限定類名 來訪問
以下是一個例子:
csc /r:TestV1=test.dll /r:TestV2=test20.dll mytest.cs //編譯指定extern alias
extern alias TestV1;
extern alias TestV2;
TestV1::Outer.Middle.Class1 …
TestV2::Outer.Middle.Class1
小結
自此,我們就大致講解完了關於namespace的一些知識點:
定義命名空間使用命名空間正確理解命名空間含義使用命名空間alias解決簡單類名沖突使用extern alias解決Assembly中的完整類名沖突
如果覺得本文對你有幫助的話,歡迎給老白點贊收藏關注^_^