C# 10 新特性 —— 命名空间的变化
Intro
C# 10 针对命名空间做了一些改变,主要是 Global Usings 和 File-scoped Namespace,我们前面分享的示例其实也是用到了这些变化,之前也写过一篇文章介绍 .NET 6 的隐式命名空间引用 .NET 6 中的隐式命名空间引用,下面我们结合示例着重介绍一下 C# 特性吧
Global Using
有尝试过 .NET 6 的小伙伴应该都知道了,新的项目模板非常的简洁
Console 项目模板中 Program.cs
只有一行代码,没有 Main
方法,没有命名空间引用 Console.WriteLine("Hello world")
WebApi 项目也同样的没有 Main
方法,没有命名空间引用
这得益于 C# 的新特性,C# 9 的顶级应用程序使得我们可以消除 Main
,而基于 C# 10 中 Global Usings 的隐式命名空间引用使得我们可以不需要声明部分命名空间
Global using 支持 using 的各种形式,使用示例:
global using System;
global using static System.Console; // static using
global using MyFile = System.IO.File; // alias
我们可以在任意一个文件中进行声明,声明之后 这个项目 其他文件里用到这里定义的命名空间就可以不需要使用 using
了
新的项目模板中模板文件会有一个 <ImplictUsings>enable</ImplictUsings>
,这一属性就是配置隐式命名空间功能,默认是禁用的,需要显式配置开启,对于多个项目的大项目,可以结合我们之前介绍的 Directory.Build.props
来进行项目全局配置 使用 Directory.Build 来消除项目文件中的重复配置
默认引用的命名空间和项目引用的 Sdk 有关,默认的 Microsoft.NET.Sdk
引用了下面这些命名空间
System
System.Collections.Generic
System.IO
System.Linq
System.Net.Http
System.Threading
System.Threading.Tasks
Microsoft.NET.Sdk.Web
在上面的基础上添加 ASP.NET Core 的一些常用的命名空间引用
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
其他项目类型可以参考微软的文档
除此之外,我们可以手动的在项目文件中配置自己想要添加的命名空间引用或者移除默认的命名空间引用
命名空间也可以使用变量,如下面的 $(RootNamespace)
表示项目默认的命名空间,使用隐式命名空间引用写过好多个小示例了,觉得如果项目命名空间包含在默认的命名空间引用里的话可能会更方便,所以提了一个 issue,不过暂时还没回复,不确定是否会考虑,感兴趣的可以参考 https://github.com/dotnet/sdk/issues/22535,这里我就自己配置一下,通过项目文件也可以配置 using static
和 using alias
,示例如下
<ItemGroup><Using Include="$(RootNamespace)"/><Using Include="WeihanLi.Common.Helpers"/><Using Include="WeihanLi.Common.Helpers.InvokeHelper" Static="true"/><Using Include="WeihanLi.Common.Logging"/><Using Include="System.IO.File" Alias="MyFile"/><Using Remove="System.Net.Http" />
</ItemGroup>
在项目文件中配置的 Using
最终会生成一个 Global Usings 的文件参与到项目的编译,生成的文件位置在 obj/Debug/net6.0
目录下,文件名称是 项目名+ ".GlobalUsings.g.cs",如下图:
File-scoped namespace
除了引用的命名空间,类型的命名空间也发生了变化,针对类型的声明也做了一些优化,原来我们创建一个类型可能是这样的
namespace Test
{public class Class1{}
}
在 C# 10 中,我们可以使用一行代码来声明 namespace
,声明类型所在的命名空间
namespace Test;
public class Class1
{
}
这样就没有那么多的缩进了,代码看起来更加简单一些,而且对于我来说写一些小示例,拷贝起来更方便了,因为一般文章中不会去写代码的所有内容,拷贝了之后再缩进有时候可能会出现格式的问题,使用这个特性之后就没有那么多的缩进了,有些情况下就可以直接复制粘贴了
File-scoped namespace 使用起来就是这么简单,但也是有限制的
一个文件中只能声明一个
namespace
,如果一个文件中想要有两个命名空间不能使用 file-scoped namespace不能和之前命名空间用法一起使用
从以往的项目来看,这两个限制还是很少会遇到的,绝大多数场景都是可以用的
More
Global usings 能够让我们的代码看起来更加简洁简单,全局的类型别名也成为可能,常规的使用还可以,但不要滥用,在代码里搞出来很多神奇的代码,可能就会有同事想要骂人了,比如说搞很多 A、B、C 等不明其意的别名,代码里全是看不懂类型的代码。。。
还有一个小细节,Global usings 和 Implicit usings 可以结合使用,上面提到的输出一个 GlobalUsings 的只包含项目文件中定义的 global usings,自己写在代码里的不会输出,和自己在代码里的 global usings 可以结合使用
对于 File-scoped namespace,推荐一个小技巧,如果你的项目使用了 Editorconfig(如果没有强烈推荐一下),可以在 editorconfig 中使用 csharp_style_namespace_declarations=file_scoped:suggestion
来配置命名空间的样式使用 file-scoped namespace,这样在 VS 中新建类时就会自动使用 file-scoped namespace 这样的写法,可以参考这个项目 https://github.com/WeihanLi/SparkTodo
References
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/globalusingdirective?WT.mc_id=DT-MVP-5004222
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/file-scoped-namespaces?WT.mc_id=DT-MVP-5004222
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/lambda-improvements?WT.mc_id=DT-MVP-5004222
https://docs.microsoft.com/en-us/dotnet/core/compatibility/sdk/6.0/implicit-namespaces-rc1?WT.mc_id=DT-MVP-5004222
https://github.com/WeihanLi/SparkTodo
https://github.com/WeihanLi/SamplesInPractice/blob/master/CSharp10Sample/CSharp10Sample.csproj
.NET 6 中的隐式命名空间引用