首页 / C# 入门教程 / 命名空间与 using 指令

C# 入门教程

命名空间与 using 指令

本教程共 100 篇 · 第 37 篇 · 更新于 2026-07-31 · 约 12 分钟阅读

C#C# 入门教程命名空间usingglobal using

37. 命名空间与 using 指令

本节目标:学完你能用命名空间给代码分门别类,并看懂文件范围与 global using 的写法。

项目一大,难免出现两个都叫 Person 的类。C# 用**命名空间(namespace)**给类型划「抽屉」,相同名字放进不同抽屉就不打架。而 using 指令让你少写一长串前缀。

为什么需要命名空间

命名空间把相关的类、结构、接口归到同一个逻辑分组下。它的核心价值是两点:避免同名冲突、让代码结构清晰。比如 System 下是基础类型,System.Collections.Generic 下是泛型集合。

传统的块范围命名空间

最经典的写法是用花括号把类型包起来。一个文件里可以写多个命名空间。

namespace DemoA
{
    class Greeter
    {
        public void Hi() => Console.WriteLine("你好,A");
    }
}

namespace DemoB
{
    class Greeter
    {
        public void Hi() => Console.WriteLine("你好,B");
    }
}

两个 Greeter 因抽屉不同而共存。使用时写全名 DemoA.Greeter 就能精确指定。

文件范围命名空间(C# 10)

C# 10 起推荐文件范围命名空间(file-scoped namespace):一行 namespace X; 写在文件顶部,后面所有类型自动归入 X,不必再套花括号。一个文件只能有一个。

namespace CSharpDemo;

class Greeter
{
    public void Hi() => Console.WriteLine("你好");
}

var g = new Greeter();
g.Hi();
Note

本书绝大多数示例都用这种写法,因为它最贴近 .NET 10 的默认模板,省掉一层缩进。块范围写法适合「一个文件塞多个命名空间」的特殊情况。

using 指令:省去前缀

每次都写 System.Collections.Generic.List 太累。在文件顶部加 using 指令,就能直接用短名。

using System.Collections.Generic;

namespace CSharpDemo;

List<string> names = ["小明", "小红"];
Console.WriteLine(names[0]);
Tip

你可能发现本书示例从没写 using System;。那是因为 .NET 10 模板自带一组隐式全局 using,下面会讲。

using 别名:给长名字起外号

遇到特别长的类型名,可用 using 别名 = 全名; 起个短名,尤其泛型嵌套时很解压。

using Dict = System.Collections.Generic.Dictionary<string, int>;

namespace CSharpDemo;

Dict counts = new();
counts["苹果"] = 3;
Console.WriteLine(counts["苹果"]);
Note

别名只对当前文件有效,且必须放在文件范围的 namespace 之前(或放在块范围命名空间内部顶部)。

global using(C# 10):整个项目通用

普通 using 只管一个文件。global using 让某个命名空间对整个项目的所有文件生效,通常集中放在一个 GlobalUsings.cs 里。

// GlobalUsings.cs
global using System;
global using System.Collections.Generic;
global using System.Linq;

从 .NET 6 起的模板,默认就隐式包含了一组 global using(含 SystemSystem.LinqSystem.Collections.Generic 等)。所以你新建控制台项目时,连 using System; 都不用写,ConsoleList 直接可用。

Tip

想看项目里到底隐式引入了哪些命名空间,打开 obj/Debug/.../项目名.GlobalUsings.g.cs 文件即可。也可以自己在 .csproj 里用 <Using Include="X" /> 增删。

完全限定名:冲突时的退路

当两个命名空间里都有同名类型,using 会引起歧义。此时写明「全路径」最稳妥。

var a = new DemoA.Greeter();   // 明确用 A 里的
var b = new DemoB.Greeter();   // 明确用 B 里的
Note

新手踩坑:加了 using 仍报「类型不明确」,多半是引入了两个都含该类型的命名空间。用完全限定名,或给其中一个起 using 别名 即可化解。

嵌套命名空间

命名空间还能分层,用点号表达层级:Company.App.User。它等价于一层套一层的块范围写法,让大型项目结构分明。

命名空间与文件夹的关系

命名空间不必和文件夹一一对应,但业界约定「命名空间层级 ≈ 文件夹层级」,例如 MyApp.User 放在 MyApp/User/ 下。遵守约定能让别人一眼定位类型所在文件,也避免命名空间乱成一团。

using static:直接调用静态成员

using static 能把某个类的静态成员「摊平」到当前文件,连类名都不用写。最典型的就是 Math

using static System.Math;

namespace CSharpDemo;

Console.WriteLine(Sqrt(16));     // 4,不必写 Math.Sqrt
Console.WriteLine(Max(3, 7));    // 7
Tip

using static 很方便,但滥用会让读者猜不出 Sqrt 来自哪个类。只对高频、众所周知的类(如 MathConsole)使用较安全。

自己管理 global using

若某命名空间在几乎所有文件都要用,可在 GlobalUsings.cs 里加一行 global using,省去每个文件重复写。但别什么都全局引入——引入越多,名字冲突概率越高。

// GlobalUsings.cs
global using System.Globalization;

别名化解更隐蔽的冲突

当两个命名空间里的同名类型让你头疼,给其中一个起别名最干净:

using WinForms = System.Windows.Forms;
using WebForms = System.Web.UI.WebControls;

var btn1 = new WinForms.Button();
var btn2 = new WebForms.Button();   // 两个 Button 各归各家
Note

常见坑:以为改了命名空间名字,类型就自动换成另一份。类型唯一身份是「全限定名」,同全名才是同一个类,和文件放哪无关。

在项目文件里配置 global using

除了写 GlobalUsings.cs,也能在 .csproj 里声明全局引入,效果相同:

<ItemGroup>
  <Using Include="System.Globalization" />
</ItemGroup>

同理可用 <Using Remove="..." /> 删掉模板默认引入的某个命名空间。

Note

全局引入越多,名字冲突概率越高。只把「几乎所有文件都用」的命名空间全局化,其余留在单文件 using

命名空间的命名约定

大型项目常用「公司.产品.分层」的层级,例如 Contoso.Shop.WebContoso.Shop.Data。层级清晰,且和文件夹对应后,定位类型飞快。

  • 顶层:组织或公司名,避免和别的库撞名;
  • 中层:产品/项目名;
  • 底层:按职责分(Model、Service、Web 等)。

using 指令放哪

using 指令必须写在文件顶部、任何类型声明之前;若用文件范围命名空间,则放在 namespace X; 这一行之前。放错位置编译器会报错。

using System.Text;          // 正确:在 namespace 之前

namespace CSharpDemo;

// using System.IO;         // 错误位置示例(请勿照搬)
Tip

常见坑:以为改了命名空间名字,类型就自动换成另一份。类型唯一身份是「全限定名」,同全名才是同一个类,和文件放哪无关。

命名空间与程序集的关系

命名空间和**程序集(assembly,即编译出的 .dll/.exe)**是两个不同维度。命名空间是「逻辑分组」,程序集是「物理打包」。一个程序集里可以包含多个命名空间,一个命名空间也能分散在多个程序集里。所以「改了命名空间名字」不会让类型自动变成另一份——类型的唯一身份是「全限定名」,同全名才是同一个类。

Note

新手常误以为「命名空间相同 = 同一个类」。不一定。两个项目里都写了 namespace CSharpDemo; class Person,编译后是两个不同的 Person,引用时要用全名或别名区分。

什么时候该新建命名空间

不用一两个类就开一个命名空间。通常当你开始按职责切分模块时再分:

  • 业务逻辑(Service)、数据模型(Model)、界面(Web/View)天然分开;
  • 某个模块大到需要「再分一层」时,用点号往下铺,如 MyApp.Order.Processing
  • 想避免和第三方库撞名时,用自己的顶层命名空间(公司/产品名)包住。
Tip

别把所有类塞进一个命名空间。「分太细」和「不分」都不好:分太细引入成本上升,完全不分则同名冲突风险高。按「可独立理解的一坨职责」来分最舒服。

最佳实践汇总

  1. 层级对齐文件夹MyApp.UserMyApp/User/,别人定位类型飞快。
  2. 全局引入要克制global using 只放「几乎所有文件都用」的命名空间;引入越多冲突概率越高。
  3. 冲突写全名:两个命名空间都有同名类型时,用完全限定名或 using 别名 化解。
  4. using 写在 namespace 之前:文件范围命名空间 namespace X; 出现前,先放 using
Warning

滥用 using static 会让读者猜不出 SqrtMax 来自哪个类。只对高频且众所周知的类(如 MathConsole)使用,其余老老实实写类名。

小结

命名空间给类型分抽屉,避免同名冲突并理清结构,习惯上用「公司.产品.分层」并与文件夹层级对应。块范围 namespace X { } 兼容老代码,文件范围 namespace X; 是 C# 10 起的推荐写法,且 using 指令要写在它之前。普通 using 省单文件前缀,using static 直接调静态成员,using 别名 给长名或冲突类型起外号,global using(可在 .csproj 配)让引入对整个项目生效——.NET 10 模板默认就带一组隐式全局 using,这正是本书示例不写 using System; 的原因。遇到冲突,写完全限定名最稳。