2 分钟阅读

命名空间(namespace)可以说是C#(甚至整个.NET生态)中最早确定的概念了, 一直以来,所有的文档和教科书都会说:

每个 .cs 文件都需要在前几行使用 using 语句来导入必要的命名空间

而各大工具的模板也会相应的在生成的 .cs 文件的前几行加上类似如下的 using 语句

1
2
3
using System;
using System.Collections.Generic;
using System.Linq;

甚至有些工程的 using 有几十句的,而其中不乏全项目各工程都重复写的,比如流行的 Orchard CMS 就这么一段: Orchard CMS namespace using

是不是看着很枯燥!前几年在写 kotlin 的时候甚至在想,如果 C# 也有类似于 Java 的 import javax.swing.*; 就好了, 现在回想,这不是一个概念,在 C# 的概念里, using System 实际就是全部引入的意思,当时太年轻,想得太简单了。

后来随着系统架构工作的深入,慢慢理解语言领域的演进是更加谨慎的, 而最佳反例可能就是angular了吧,从曾经几乎统治前端的辉煌到被挤出前三的寂寥,无非是技术决策的一次任性。

言归正传,C# 10 的命名空间的改进体现在两个地方,显然不会是颠覆性的,甚至可以说也是语法糖层面的小点心,但是用了之后还是会心一笑,真香定律还是妥妥的。 真香

首先,引入了一个新的语句,它就是 global using ,这意味着开发人员需要且仅需要在任何一个 .cs 文件中显示指定一次所需的 namespace 导入, 整个项目的其他文件就能自动使用这些 namespace 了。 譬如说,你可以简单的把这些 global using 放在 Program.cs 文件中,但就个人而言, 强烈推荐把这些语句放到一个单独的文件,并且命名成类似 GlobalUsings.cs 或者 GlobalNamespaces.cs,内容如下:

1
2
3
global using System;
global using System.Collections.Generic;
global using System.Linq;

其次,对于所有的 target.NET 6.0 的项目,默认的就启用了 C# 10, 此时编译器可以自动在 obj 目录生成一些隐式全局导入,当然,实际导入的 namespace 受不同的 SDK 影响,如下表:

SDK 隐式导入的 namespace
Microsoft.NET.Sdk System
System.Collections.Generic
System.IO
System.Linq
System.Net.Http
System.Threading
System.Threading.Tasks
Microsoft.NET.Sdk.Web 包括 Microsoft.NET.Sdk,并且额外引入:
System.Net.Http.Json
Microsoft.AspNetCore.Builder
Microsoft.AspNetCore.Hosting
Microsoft.AspNetCore.Http
Microsoft.AspNetCore.Routing
Microsoft.Extensions.Configuration
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Hosting
Microsoft.Extensions.Logging
Microsoft.NET.Sdk.Worker 包括 Microsoft.NET.Sdk,并且额外引入:
Microsoft.Extensions.Configuration
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Hosting
Microsoft.Extensions.Logging

那么,这个隐式导入时如何实现的呢?其实前面已经提到过,编译器在编译之前自动生成了一个文件,放在 obj\Debug\net6.0\<ProjectName>.GlobalUsings.g.cs,看起来内容如下:

1
2
3
4
5
6
7
8
9
// <autogenerated />
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;

看起来比较完美,但是有人肯定会说,我要定制呀,比如我不想要 System.Threading,但是需要 System.Numerics,那么这个自动生成该如何控制呢?

嘿,这个定制肯定是有的,但没有友好的UI界面来编辑,这个需要手动编辑 project 文件(通常是 <ProjectName>.csproj ),如下图加粗所示:  Implicit Usings in csproj

当然,这里也可以禁用自动生成隐式导入这个功能,只需要在工程文件 csproj 中删除这一句话就可以了 <ImplicitUsings>enable</ImplicitUsings>