我一直在經過某些C#代碼運行StyleCop ,而且一直在報告個人using
指令應該在名稱空間內。 函數
是否有技術上的理由將using
指令放在名稱空間的內部而不是外部? ui
將其放在名稱空間中會使聲明在文件的該名稱空間中處於本地狀態(若是文件中有多個名稱空間),可是若是每一個文件只有一個名稱空間,則它們在外部仍是外部都不會有太大的區別。在名稱空間中。 spa
using ThisNamespace.IsImported.InAllNamespaces.Here; namespace Namespace1 { using ThisNamespace.IsImported.InNamespace1.AndNamespace2; namespace Namespace2 { using ThisNamespace.IsImported.InJustNamespace2; } } namespace Namespace3 { using ThisNamespace.IsImported.InJustNamespace3; }
根據Hanselman-使用指令和程序集加載...以及其餘此類文章,技術上沒有區別。 code
個人偏好是將它們放在名稱空間以外。 blog
當您但願使用別名時,在名稱空間中放置using語句存在問題。 別名不能從較早的using
語句中受益,必須徹底限定。 接口
考慮: ip
namespace MyNamespace { using System; using MyAlias = System.DateTime; class MyClass { } }
與: 文檔
using System; namespace MyNamespace { using MyAlias = DateTime; class MyClass { } }
若是您有一個冗長的別名,例如如下內容(這就是我發現問題的方式),則這一點尤爲明顯: get
using MyAlias = Tuple<Expression<Func<DateTime, object>>, Expression<Func<TimeSpan, object>>>;
在名稱空間內using
語句時,它忽然變成: 編譯器
using MyAlias = System.Tuple<System.Linq.Expressions.Expression<System.Func<System.DateTime, object>>, System.Linq.Expressions.Expression<System.Func<System.TimeSpan, object>>>;
不漂亮。
根據StyleCop文檔:
SA1200:在名稱空間中使用DirectivesMustBePlacedWith
緣由AC#using指令放置在名稱空間元素以外。
規則說明當將using指令或using-alias指令放置在名稱空間元素以外時,會發生違反此規則的狀況,除非文件不包含任何名稱空間元素。
例如,如下代碼將致使兩次違反此規則。
using System; using Guid = System.Guid; namespace Microsoft.Sample { public class Program { } }
可是,如下代碼不會致使任何違反此規則的狀況:
namespace Microsoft.Sample { using System; using Guid = System.Guid; public class Program { } }
這段代碼能夠乾淨地編譯,沒有任何編譯器錯誤。 可是,尚不清楚正在分配哪一個版本的Guid類型。 若是將using指令移至名稱空間內,以下所示,則會發生編譯器錯誤:
namespace Microsoft.Sample { using Guid = System.Guid; public class Guid { public Guid(string s) { } } public class Program { public static void Main(string[] args) { Guid g = new Guid("hello"); } } }
代碼在包含Guid g = new Guid("hello");
的行中發現如下編譯器錯誤時失敗Guid g = new Guid("hello");
CS0576:命名空間「 Microsoft.Sample」包含與別名「 Guid」衝突的定義
該代碼建立System.Guid類型的別名Guid的別名,並使用匹配的構造函數接口建立本身的類型Guid的別名。 後來,代碼建立了Guid類型的實例。 要建立此實例,編譯器必須在Guid的兩個不一樣定義之間進行選擇。 當using-alias指令放置在namespace元素以外時,編譯器將選擇在本地命名空間內定義的Guid的本地定義,並徹底忽略在namespace以外定義的using-alias指令。 不幸的是,這在閱讀代碼時並不明顯。
可是,當using-alias指令位於名稱空間內時,編譯器必須在兩個不一樣的,衝突的Guid類型之間進行選擇,二者都在同一名稱空間內定義。 這兩種類型都提供了匹配的構造函數。 編譯器沒法作出決定,所以會標記編譯器錯誤。
將using-alias指令放置在名稱空間以外是一種很差的作法,由於在這種狀況下(實際上並不知道實際使用的是哪一個版本),這可能致使混亂。 這可能會致使可能難以診斷的錯誤。
在名稱空間元素中放置using-alias指令能夠消除這種狀況,使之成爲錯誤的來源。
將多個名稱空間元素放在一個文件中一般是一個壞主意,可是若是這樣作,那麼最好將全部using指令放在每一個名稱空間元素中,而不是全局地放在文件頂部。 這將嚴格限制名稱空間的範圍,也將有助於避免上述行爲。
重要的是要注意,當使用放置在名稱空間以外的指令編寫代碼時,在將這些指令移至名稱空間內時應格外當心,以確保這不會改變代碼的語義。 如上所述,將using-alias指令放置在namespace元素中,可以使編譯器以衝突的方式進行選擇,方式是將指令放在命名空間以外時不會發生。
如何解決衝突要解決違反此規則的問題,請在命名空間元素內移動全部using指令和using-alias指令。
二者之間實際上存在(細微)差別。 假設您在File1.cs中具備如下代碼:
// File1.cs using System; namespace Outer.Inner { class Foo { static void Bar() { double d = Math.PI; } } }
如今,假設有人將另外一個文件(File2.cs)添加到項目中,以下所示:
// File2.cs namespace Outer { class Math { } }
編譯器先搜索Outer
而後再using
命名空間以外的指令查看它們,所以它將找到Outer.Math
而不是System.Math
。 不幸的是(或者幸運的是?), Outer.Math
沒有PI
成員,所以Outer.Math
如今已損壞。
若是將using
放在名稱空間聲明中,則狀況會發生變化,以下所示:
// File1b.cs namespace Outer.Inner { using System; class Foo { static void Bar() { double d = Math.PI; } } }
如今,編譯器在搜索Outer
以前先搜索System
而後找到System.Math
,一切都很好。
有人會認爲Math
對於用戶定義的類來講多是個壞名字,由於System
已經有一個了。 這裏要說的就是這樣是有區別的,它會影響你的代碼的可維護性。
有趣的是,若是Foo
在命名空間Outer
而不是Outer.Inner
,會發生什麼。 在這種狀況下,不管using
在何處,在Outer.Math
中添加Outer.Math
破壞Outer.Math
。 這意味着編譯器在查看任何using
指令以前先搜索最裏面的命名空間。