第 25 节 · 第三部分 面向对象

命名空间(Namespace)

命名空间给类名加前缀,不同命名空间里的同名类互不冲突;用完全限定名或 using 找到它们。

时长 7:28授课计划第 9 周

学完这一节,你能

讲义

跟着视频的顺序写。每行代码都有注释,代码和运行结果都是实际运行得到的。

1命名空间是什么

类越写越多,名字难免撞车。命名空间把一组名字和别的名字分隔开:两个命名空间里各有一个同名的类,互不冲突。

这像电脑里的文件夹:同一个文件夹里不能有两个同名文件,放在不同文件夹里就可以重名。但命名空间不是真的文件夹,它只是加在类名前面的一段前缀。

2定义命名空间

先写关键字 namespace,后面跟名字,再用一对花括号把代码包起来:

C# · 第 2-11 行
2namespace 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.namespace_cl。命名空间里能直接放的是类型(类、结构体、接口、枚举、委托)和别的命名空间;方法、变量必须写在某个类里面。把方法直接写进命名空间:

C# · 第 12-16 行
12namespace second_space// 定义命名空间 second_space
13{// second_space 开始
14 void func()// 方法直接写在命名空间里,没有放进类
15 {// func 开始
16 }// func 结束
编译报错
Program.cs(14,9): error CS0116: 命名空间不能直接包含字段、方法或语句之类的成员

3第一个实例

两个命名空间里各有一个 namespace_cl,名字完全一样。Main 用带着命名空间名的写法创建对象,这种写法叫完全限定名:

C# · 第 26-29 行
26first_space.namespace_cl fc = new first_space.namespace_cl();// 完全限定名:到 first_space 里取 namespace_cl
27second_space.namespace_cl sc = new second_space.namespace_cl();// 换到 second_space 里取同名的 namespace_cl
28fc.func();// 调用 first_space 那个类的 func
29sc.func();// 调用 second_space 那个类的 func
查看完整代码(32 行)
C#
1using System;// 引入 System 命名空间,后面能直接写 Console
2namespace 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 结束
12namespace 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 结束
22class 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:

C# · 第 12-14 行
12namespace 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.Console。文件开头写了 using System;,就只用写 Console:

C# · 第 1 行
1using System;// using:把 System 命名空间引进来,里面的类报名字就能用
C# · 第 6-7 行
6Console.WriteLine ("Hello there");// 有 using System,直接写 Console
7System.Console.WriteLine("Hello there");// 完全限定名写法,不靠 using 也能用
运行结果
Hello there
Hello there

不写 using System 也能运行?

新项目的工程文件(.csproj,记录项目设置的文件)里默认有一行 <ImplicitUsings>enable</ImplicitUsings>,意思是打开隐式 using:编译时自动生成 global using System; 等 7 行(global using 是 C# 10 起的写法,对整个项目都生效)。所以下面这行在没写 using System; 的文件里照样输出 Hello there:

C# · 第 5 行
5Console.WriteLine("Hello there");// 文件里没写 using System,靠隐式 using 才能用 Console

把这一行改成 disable 关掉隐式 using,同样的代码就报错:

C# · 第 5 行
5Console.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 里直接写短名,输出还是那两行。

C# · 第 2-3 行
2using first_space;// 引入 first_space,里面的 abc 能直接写
3using second_space;// 引入 second_space,里面的 efg 能直接写
C# · 第 29-32 行
29abc fc = new abc();// 不用写 first_space.abc,直接写 abc
30efg sc = new efg();// 同样直接写 efg
31fc.func();// 输出 Inside first_space
32sc.func();// 输出 Inside second_space
查看完整代码(35 行)
C#
1using System;// 引入 System 命名空间
2using first_space;// 引入 first_space 命名空间
3using second_space;// 引入 second_space 命名空间
4
5namespace 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 结束
15namespace 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 结束
25class 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 了,只写短名:

C# · 第 29 行
29namespace_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.namespace_cl。

6嵌套命名空间

命名空间里还可以再定义命名空间,访问时用点 . 一层一层往下写。下面 SomeNameSpace 里套着 Nested,类的完整名字是 SomeNameSpace.Nested.NestedNameSpaceClass。Main 本身就在 SomeNameSpace 里,所以从 Nested 写起就够:

C# · 第 9-12 行
9static void Main()// Main 写在 SomeNameSpace 的 MyClass 里
10{// Main 开始
11 Console.WriteLine("In SomeNameSpace");// 先打印 In SomeNameSpace
12 Nested.NestedNameSpaceClass.SayHello();// 再往下一层,调用 Nested 里的 SayHello
查看完整代码(27 行)
C#
1using System;// 引入 System 命名空间
2using SomeNameSpace;// 引入 SomeNameSpace(这个例子里删掉也能运行)
3using SomeNameSpace.Nested;// 引入里面那层 Nested(这个例子里删掉也能运行)
4
5namespace 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;,连 Nested 都能省掉,输出不变:

C# · 第 12 行
12NestedNameSpaceClass.SayHello();// 靠 using SomeNameSpace.Nested,直接写类名

反过来,把开头那两行 using 都删掉,原来的 Nested.NestedNameSpaceClass.SayHello(); 也照样运行,输出还是那两行:

C# · 第 1-3 行
1using System;// 只留 using System,两行 using SomeNameSpace… 都删了
2
3namespace SomeNameSpace// 后面的代码和原例子一样

7小结

  • 命名空间给类名加上前缀,不同命名空间里的同名类互不冲突;同一个命名空间里不能重名。
  • 命名空间里直接放类型和别的命名空间,方法、变量要写在类里。
  • 用完全限定名,或者用 using 引入,都能找到别的命名空间里的类;两个 using 里有同名类时,要写回完全限定名。
  • 命名空间能一层套一层,用点逐层访问。

下一节:同一份代码,能不能让编译器在编译之前,先决定哪几行要编译、哪几行跳过?

检查题

5 道,选完马上告诉你对不对。

  1. 1namespace Tools { void Hello() { } } 能编译吗?

  2. 2first_space、second_space 里各有一个 namespace_cl,Main 里写 first_space.namespace_cl fc = new first_space.namespace_cl(); 再 fc.func();,打印什么?

  3. 3文件开头同时写了 using first_space; 和 using second_space;,两个命名空间里都有 namespace_cl,Main 里写 namespace_cl fc = new namespace_cl(); 会怎样?

  4. 4把第二段 namespace second_space 改名成 namespace first_space,两段里各有一个 namespace_cl,会怎样?

  5. 5新建的控制台项目里没写 using System;,Console.WriteLine 还能用吗?