C# 10 新功能 - 全局命名空间和隐式导入
命名空间(namespace)可以说是C#(甚至整个.NET生态)中最早确定的概念了, 一直以来,所有的文档和教科书都会说:
每个
.cs
文件都需要在前几行使用using
语句来导入必要的命名空间
而各大工具的模板也会相应的在生成的 .cs
文件的前几行加上类似如下的 using
语句
1
2
3
using System;
using System.Collections.Generic;
using System.Linq;
甚至有些工程的 using
有几十句的,而其中不乏全项目各工程都重复写的,比如流行的 Orchard CMS 就这么一段:
是不是看着很枯燥!前几年在写 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
),如下图加粗所示:
当然,这里也可以禁用自动生成隐式导入这个功能,只需要在工程文件 csproj 中删除这一句话就可以了
<ImplicitUsings>enable</ImplicitUsings>