关于对 C# 中 ImplicitUsings,GlobalUsings 的讨论
2026/7/22 15:22:59 网站建设 项目流程

微软 SDK 团队的 Tim Heuer 提出了一个想法:从 .NET 10 开始,把 enable 和 enable 从项目模板移入 SDK 核心 targets 中,让它们变成真正意义上的“默认开启且无需在 .csproj 中显式出现”。

他的理由是:

自 .NET 6 起,所有模板都默认写了这两行,对现代框架来说已是“不必要的样板”;
.NET 10 引入了单文件运行(dotnet run app.cs)和 dotnet project convert,新手从一个 .cs 文件转成项目时,突然冒出来的属性会成为“学习绊脚石”;
SDK 应该更“武断(opinionated)”一点,推动现代 C# 开发的前进方向。
读到这里,我心里警铃大作——如果这落地了,未来想关闭隐式 using 的阻力会更大,甚至很多开发者会根本不知道这个开关的存在。

围观大佬们的“掰头”
点开讨论区,我发现社区并不是一边倒地鼓掌,反而有不少资深开发者提出了尖锐的反对意见,这也成了我坚定立场的关键参考。

反对派的声音(我站这边)
TheBuzzSaw(2025-05-29)
直言不认同 ImplicitUsings,每个新项目都会关掉它。他认为“我不想要在每个文件里都生效的命名空间,也不想让名字以不显眼的方式突然冲突”。在他看来,csproj 里那一行的存在不是样板,而是对项目重要决策的显式声明。

londospark(2025-05-30)
附议上述观点,觉得“知道类型从哪来非常有用”,开启 ImplicitUsings 是用极小收益换取 Bug 的温床。好用的 IDE 本来就会自动补 using,没必要把这个认知负担转嫁给读者。

Frulfump(2025-05-30)
进一步指出隐患:一旦 ImplicitUsings 默认开启,开发者容易在 GlobalUsings.cs 里无脑加更多命名空间,导致“黑盒依赖”问题恶化。而且在 GitHub / Azure DevOps 做 PR Review 时,我们往往不在 IDE 里,看不到悬停提示,文件顶部的显式 using 才是代码自描述的底线。

提案方的顾虑与妥协
Tim Heuer 也承认,这会带来升级破坏性(尤其是那些“删掉了属性而非显式设 disable”的旧项目)。他提到可能通过 if (TFM >= net10.0) 之类的条件来缓解,但核心目的仍是“清理模板噪音,让默认更隐式”。

我的复盘:为什么“隐式”在这里不成立
结合大佬们的讨论,回头看 C# 10 以来一直被宣传的 ImplicitUsings,我在 C# 14 / .NET 10 的项目里重新评估了它,结论是:关掉它。

  1. “Explicit is better than implicit”不是废话
    当一个 .cs 文件顶部没有 using System; 时,你失去的是一眼看清依赖的能力。几个月后回看代码,或者新人接手,没法从文件本身推断它依赖了哪些命名空间。对比一下:

// 隐式:Path 来自 System.IO?还是某个第三方库的 Path?
var p = Path.Combine(a, b);
// 显式:依赖一目了然
using System.IO;
var p = Path.Combine(a, b);
2. 命名空间冲突的隐性炸弹
System.IO.Path vs iTextSharp.text.pdf.parser.Path,当项目引用变多,隐式 using 会让你在不知情的情况下绑定到某一个。编译器会选,但你未必想要那个选择。显式声明把歧义在书写时就暴露出来。

  1. obj/*.GlobalUsings.g.cs 是看不见的文件
    ImplicitUsings 的本质是 SDK 在 obj/ 下生成一个全局 using 文件。没人会日常去翻 obj/,Ctrl+Shift+F 也搜不到这些依赖声明。把依赖知情权交给一个构建中间文件,是对可维护性的透支。

  2. 默认注入的比你以为的多
    以 Microsoft.NET.Sdk 为例,默认隐式注入的包括:
    System, System.Collections.Generic, System.IO, System.Linq, System.Net.Http, System.Threading, System.Threading.Tasks
    Web / Worker SDK 还会追加一大把 Microsoft.AspNetCore.* 和 Extensions.*。
    你可能只是写个简单控制台,却在无意中调用了 LINQ 或 HttpClient,自己毫无察觉——直到迁移到一个没有隐式 using 的环境编译崩塌。

我的最终方案
关掉 ImplicitUsings,但合理使用 GlobalUsings(显式版)。

第一步:关开关

disable 第二步:手写一个受控的 GlobalUsings.cs 不要靠 SDK 魔法,自己在项目根目录建一个 GlobalUsings.cs,只放你审过的、真正想全局共享的命名空间:

// GlobalUsings.cs
global using System;
global using System.Collections.Generic;
global using System.IO;
global using System.Threading.Tasks;
// 刻意不放 System.Linq、System.Net.Http —— 这两个我希望在用到时显式声明
这样做的好处是:

✅ 依赖集合集中、可见、可审查
✅ 没有 obj/ 下的黑盒生成文件
✅ 仍然减少部分文件顶部噪音,但在完全可控的前提下
✅ 就算 .NET 10 的 #49182 落地,你的项目已经站在清晰的一边
如果某些 SDK 默认会隐式引入你不想用的,也可以在 csproj 里精确剔除:

结语:我的立场 Tim Heuer 的提案出发点是好的——降低新手门槛、减少模板噪音。但用“隐式”解决“显式带来的认知负担”,本质上是在把成本从“写代码的人”转移给“读代码和排错的人”。

在 #49182 的讨论里,TheBuzzSaw、longspark 这些老手的反对让我确认了一件事:
ImplicitUsings 作为可选特性没问题,但作为默认方向,我不买账。

代码的“简洁”是写给此刻的自己看的,代码的“清晰”是写给未来的自己和同伴看的。两者冲突时,我选清晰。

所以,在我的 C# 14 / .NET 10 项目里:

ImplicitUsings → disable
GlobalUsings.cs → 手写、受控、审查后使用

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询