1命名空间是什么
类越写越多,名字难免撞车。命名空间把一组名字和别的名字分隔开:两个命名空间里各有一个同名的类,互不冲突。
这像电脑里的文件夹:同一个文件夹里不能有两个同名文件,放在不同文件夹里就可以重名。但命名空间不是真的文件夹,它只是加在类名前面的一段前缀。
2定义命名空间
先写关键字 namespace,后面跟名字,再用一对花括号把代码包起来:
| 2 | namespace first_space | // namespace 关键字 + 名字:定义命名空间 first_space |
| 3 | { | // first_space 开始 |
| 4 | class namespace_cl | // 命名空间里放一个类 namespace_cl |
| 5 | { | // namespace_cl 类开始 |
| 6 | public void func() | // func 方法写在类里面 |
| 7 | { | // func 开始 |
| 8 | Console.WriteLine("Inside first_space"); | // 打印自己在哪个命名空间 |
| 9 | } | // func 结束 |
| 10 | } | // namespace_cl 类结束 |
| 11 | } | // first_space 结束 |
要用里面的东西,就写“命名空间名.名字”,比如 first_space.。命名空间里能直接放的是类型(类、结构体、接口、枚举、委托)和别的命名空间;方法、变量必须写在某个类里面。把方法直接写进命名空间:
| 12 | namespace second_space | // 定义命名空间 second_space |
| 13 | { | // second_space 开始 |
| 14 | void func() | // 方法直接写在命名空间里,没有放进类 |
| 15 | { | // func 开始 |
| 16 | } | // func 结束 |
Program.cs(14,9): error CS0116: 命名空间不能直接包含字段、方法或语句之类的成员
3第一个实例
两个命名空间里各有一个 namespace_cl,名字完全一样。Main 用带着命名空间名的写法创建对象,这种写法叫完全限定名:
| 26 | first_space.namespace_cl fc = new first_space.namespace_cl(); | // 完全限定名:到 first_space 里取 namespace_cl |
| 27 | second_space.namespace_cl sc = new second_space.namespace_cl(); | // 换到 second_space 里取同名的 namespace_cl |
| 28 | fc.func(); | // 调用 first_space 那个类的 func |
| 29 | sc.func(); | // 调用 second_space 那个类的 func |
查看完整代码(32 行)
| 1 | using System; | // 引入 System 命名空间,后面能直接写 Console |
| 2 | namespace first_space | // 定义命名空间 first_space |
| 3 | { | // first_space 开始 |
| 4 | class namespace_cl | // first_space 里的类 namespace_cl |
| 5 | { | // namespace_cl 类开始 |
| 6 | public void func() | // func 方法 |
| 7 | { | // func 开始 |
| 8 | Console.WriteLine("Inside first_space"); | // 打印 Inside first_space |
| 9 | } | // func 结束 |
| 10 | } | // namespace_cl 类结束 |
| 11 | } | // first_space 结束 |
| 12 | namespace second_space | // 定义命名空间 second_space |
| 13 | { | // second_space 开始 |
| 14 | class namespace_cl | // 同名的类 namespace_cl,在另一个命名空间里,不冲突 |
| 15 | { | // namespace_cl 类开始 |
| 16 | public void func() | // func 方法 |
| 17 | { | // func 开始 |
| 18 | Console.WriteLine("Inside second_space"); | // 打印 Inside second_space |
| 19 | } | // func 结束 |
| 20 | } | // namespace_cl 类结束 |
| 21 | } | // second_space 结束 |
| 22 | class TestClass | // TestClass 没写在任何命名空间里,属于全局命名空间 |
| 23 | { | // TestClass 类开始 |
| 24 | static void Main(string[] args) | // 程序入口 Main 方法 |
| 25 | { | // Main 开始 |
| 26 | first_space.namespace_cl fc = new first_space.namespace_cl(); | // 用完全限定名创建 first_space 里的对象 |
| 27 | second_space.namespace_cl sc = new second_space.namespace_cl(); | // 用完全限定名创建 second_space 里的对象 |
| 28 | fc.func(); | // 输出 Inside first_space |
| 29 | sc.func(); | // 输出 Inside second_space |
| 30 | Console.ReadKey(); | // 等待按键(现在的 VS 不写也会停住) |
| 31 | } | // Main 结束 |
| 32 | } | // TestClass 类结束 |
Inside first_space Inside second_space
同名的两个类靠前面的命名空间名分开。TestClass 没写在任何命名空间里,属于全局命名空间。
同一个命名空间里不能重名
把第二段也改名叫 first_space,两段 namespace first_space 其实是同一个命名空间,里面就有了两个 namespace_cl:
| 12 | namespace first_space | // 第二段也叫 first_space:和第一段是同一个命名空间 |
| 13 | { | // 第二段 first_space 开始 |
| 14 | class namespace_cl | // 再写一个 namespace_cl,和第一段的重名 |
Program.cs(14,10): error CS0101: 命名空间“first_space”已经包含“namespace_cl”的定义 Program.cs(16,19): error CS0111: 类型“namespace_cl”已定义了一个名为“func”的具有相同参数类型的成员
第二条 CS0111 是跟着第一条出来的:两个 namespace_cl 撞成了一个名字,里面的 func 也就重复了。
4using 关键字
Console 这个类在 System 命名空间里,完全限定名是 System.。文件开头写了 using System;,就只用写 Console:
| 1 | using System; | // using:把 System 命名空间引进来,里面的类报名字就能用 |
| 6 | Console.WriteLine ("Hello there"); | // 有 using System,直接写 Console |
| 7 | System.Console.WriteLine("Hello there"); | // 完全限定名写法,不靠 using 也能用 |
Hello there Hello there
不写 using System 也能运行?
新项目的工程文件(.csproj,记录项目设置的文件)里默认有一行 <ImplicitUsings>enable</,意思是打开隐式 using:编译时自动生成 global using System; 等 7 行(global using 是 C# 10 起的写法,对整个项目都生效)。所以下面这行在没写 using System; 的文件里照样输出 Hello there:
| 5 | Console.WriteLine("Hello there"); | // 文件里没写 using System,靠隐式 using 才能用 Console |
把这一行改成 disable 关掉隐式 using,同样的代码就报错:
| 5 | Console.WriteLine("Hello there"); | // 隐式 using 关了,又没写 using System,找不到 Console |
Program.cs(5,7): error CS0103: 当前上下文中不存在名称“Console”
以上两次运行用的是 .NET 9(9.0.315);用 .NET 10 新建项目,自动生成的也是这 7 行(实测)。
5第二个实例
用 using 把第一个例子改写:开头引入两个命名空间,两个类改名叫 abc、efg,Main 里直接写短名,输出还是那两行。
| 2 | using first_space; | // 引入 first_space,里面的 abc 能直接写 |
| 3 | using second_space; | // 引入 second_space,里面的 efg 能直接写 |
| 29 | abc fc = new abc(); | // 不用写 first_space.abc,直接写 abc |
| 30 | efg sc = new efg(); | // 同样直接写 efg |
| 31 | fc.func(); | // 输出 Inside first_space |
| 32 | sc.func(); | // 输出 Inside second_space |
查看完整代码(35 行)
| 1 | using System; | // 引入 System 命名空间 |
| 2 | using first_space; | // 引入 first_space 命名空间 |
| 3 | using second_space; | // 引入 second_space 命名空间 |
| 4 | | |
| 5 | namespace first_space | // 定义命名空间 first_space |
| 6 | { | // first_space 开始 |
| 7 | class abc | // first_space 里的类 abc |
| 8 | { | // abc 类开始 |
| 9 | public void func() | // func 方法 |
| 10 | { | // func 开始 |
| 11 | Console.WriteLine("Inside first_space"); | // 打印 Inside first_space |
| 12 | } | // func 结束 |
| 13 | } | // abc 类结束 |
| 14 | } | // first_space 结束 |
| 15 | namespace second_space | // 定义命名空间 second_space |
| 16 | { | // second_space 开始 |
| 17 | class efg | // second_space 里的类 efg |
| 18 | { | // efg 类开始 |
| 19 | public void func() | // func 方法 |
| 20 | { | // func 开始 |
| 21 | Console.WriteLine("Inside second_space"); | // 打印 Inside second_space |
| 22 | } | // func 结束 |
| 23 | } | // efg 类结束 |
| 24 | } | // second_space 结束 |
| 25 | class TestClass | // TestClass 在全局命名空间 |
| 26 | { | // TestClass 类开始 |
| 27 | static void Main(string[] args) | // 程序入口 Main 方法 |
| 28 | { | // Main 开始 |
| 29 | abc fc = new abc(); | // 有 using first_space,直接写短名 abc |
| 30 | efg sc = new efg(); | // 有 using second_space,直接写短名 efg |
| 31 | fc.func(); | // 输出 Inside first_space |
| 32 | sc.func(); | // 输出 Inside second_space |
| 33 | Console.ReadKey(); | // 等待按键(现在的 VS 不写也会停住) |
| 34 | } | // Main 结束 |
| 35 | } | // TestClass 类结束 |
两个 using 里有同名的类
如果两个类没改名,都还叫 namespace_cl,两个命名空间都 using 了,只写短名:
| 29 | namespace_cl fc = new namespace_cl(); | // 两个 using 里都有 namespace_cl,编译器不知道取哪一个 |
Program.cs(29,7): error CS0104: “namespace_cl”是“first_space.namespace_cl”和“second_space.namespace_cl”之间的不明确的引用 Program.cs(29,29): error CS0104: “namespace_cl”是“first_space.namespace_cl”和“second_space.namespace_cl”之间的不明确的引用
这时只能写回完全限定名,点名要哪个命名空间里的,比如 first_space.。
6嵌套命名空间
命名空间里还可以再定义命名空间,访问时用点 . 一层一层往下写。下面 SomeNameSpace 里套着 Nested,类的完整名字是 SomeNameSpace.。Main 本身就在 SomeNameSpace 里,所以从 Nested 写起就够:
| 9 | static void Main() | // Main 写在 SomeNameSpace 的 MyClass 里 |
| 10 | { | // Main 开始 |
| 11 | Console.WriteLine("In SomeNameSpace"); | // 先打印 In SomeNameSpace |
| 12 | Nested.NestedNameSpaceClass.SayHello(); | // 再往下一层,调用 Nested 里的 SayHello |
查看完整代码(27 行)
| 1 | using System; | // 引入 System 命名空间 |
| 2 | using SomeNameSpace; | // 引入 SomeNameSpace(这个例子里删掉也能运行) |
| 3 | using SomeNameSpace.Nested; | // 引入里面那层 Nested(这个例子里删掉也能运行) |
| 4 | | |
| 5 | namespace SomeNameSpace | // 定义外层命名空间 SomeNameSpace |
| 6 | { | // SomeNameSpace 开始 |
| 7 | public class MyClass | // 外层里的类 MyClass |
| 8 | { | // MyClass 类开始 |
| 9 | static void Main() | // 程序入口 Main 方法 |
| 10 | { | // Main 开始 |
| 11 | Console.WriteLine("In SomeNameSpace"); | // 输出 In SomeNameSpace |
| 12 | Nested.NestedNameSpaceClass.SayHello(); | // 往下一层,调用 Nested 里类的 SayHello |
| 13 | } | // Main 结束 |
| 14 | } | // MyClass 类结束 |
| 15 | | |
| 16 | // 内嵌命名空间 | |
| 17 | namespace Nested | // 在 SomeNameSpace 里面再定义命名空间 Nested |
| 18 | { | // Nested 开始 |
| 19 | public class NestedNameSpaceClass | // Nested 里的类 NestedNameSpaceClass |
| 20 | { | // NestedNameSpaceClass 类开始 |
| 21 | public static void SayHello() | // 静态方法 SayHello,用类名就能调用 |
| 22 | { | // SayHello 开始 |
| 23 | Console.WriteLine("In Nested"); | // 输出 In Nested |
| 24 | } | // SayHello 结束 |
| 25 | } | // NestedNameSpaceClass 类结束 |
| 26 | } | // Nested 结束 |
| 27 | } | // SomeNameSpace 结束 |
In SomeNameSpace In Nested
开头有了 using SomeNameSpace.,连 Nested 都能省掉,输出不变:
| 12 | NestedNameSpaceClass.SayHello(); | // 靠 using SomeNameSpace.Nested,直接写类名 |
反过来,把开头那两行 using 都删掉,原来的 Nested. 也照样运行,输出还是那两行:
| 1 | using System; | // 只留 using System,两行 using SomeNameSpace… 都删了 |
| 2 | | |
| 3 | namespace SomeNameSpace | // 后面的代码和原例子一样 |
7小结
- 命名空间给类名加上前缀,不同命名空间里的同名类互不冲突;同一个命名空间里不能重名。
- 命名空间里直接放类型和别的命名空间,方法、变量要写在类里。
- 用完全限定名,或者用
using引入,都能找到别的命名空间里的类;两个 using 里有同名类时,要写回完全限定名。 - 命名空间能一层套一层,用点逐层访问。
下一节:同一份代码,能不能让编译器在编译之前,先决定哪几行要编译、哪几行跳过?
