C# Net9的模块初始化器(Module Initializer)
Module Initializer 是为了让库/框架在程序集加载时,以 “CLR 保证的、只运行一次的、不依赖类型访问的” 方式执行初始化逻辑,从而避免静态构造函数的副作用和性能问题。
为什么需要 Module Initializer?
1. 静态构造函数的问题
- 触发时机不确定:CLR 保证在第一次访问类型前调用静态构造函数,但 你无法精确控制它什么时候运行。
- 性能开销:CLR 对静态构造函数的类型会加锁,防止并发初始化,这会带来性能损耗。
- 不能跨类型共享初始化逻辑:每个有静态构造函数的类都要单独处理,无法集中初始化。
2. 模块初始化器的优势
- 只运行一次:在程序集加载时 由 CLR 自动调用一次,不依赖任何类型访问。
- 无类型访问开销:不需要触发某个类型的静态构造函数来“顺便”初始化。
- AOT 兼容的初始化逻逻辑
using System.Runtime.CompilerServices; class Program { static void Main() { Console.WriteLine("Main"); } } class Init { [ModuleInitializer] public static void Initialize() { Console.WriteLine("Module Initializer runs before Main!"); } }
输出:
Module Initializer runs before Main!
Main
在 NativeAOT 场景里,所有必须在运行时“反射”才能完成的事情都必须提前在编译期做完。Module Initializer 就是“把编译期算好的东西在程序一启动就塞进运行时”的唯一可靠入口——它跑在 任何用户代码、任何泛型实例化、任何反射调用之前,而且 不需要触发某个类型的静态构造,因此不会引入 AOT 禁止的动态路径。
NativeAOT 的底线是 “运行时不能做任何‘发现’工作”。所有发现必须在编译期完成,而 发现结果塞进运行时的唯一零成本窗口就是 Module Initializer;
因此只要你在 AOT 模式下看到“编译期源生成器 + 运行时注册表/函数指针/缓存” 这种组合,背后几乎一定藏着一个 [ModuleInitializer]——它已经成了 AOT 生态的隐形基础设施。

浙公网安备 33010602011771号