模块初始值设定项
注意
本文是特性规范。 该规范充当该功能的设计文档。 它包括建议的规范更改,以及功能设计和开发过程中所需的信息。 这些文章将发布,直到建议的规范更改最终确定并合并到当前的 ECMA 规范中。
功能规范与已完成的实现之间可能存在一些差异。 这些差异在相关的 语言设计会议(LDM)记录中进行了记录。
可以在 规范一文中详细了解将功能规范采用 C# 语言标准的过程。
支持者问题:https://github.com/dotnet/csharplang/issues/2608
总结
尽管 .NET 平台具有 功能,它直接支持为程序集编写初始化代码(从技术上而言是模块),但它不会在 C# 中公开。 这是一种相当小众的情况,但一旦遇到了,解决办法似乎很是棘手。 有报道称,许多客户(包括 Microsoft 内部和外部)都在努力解决这个问题,毫无疑问,还有更多未记录的案例。
动机
- 使库能够在加载时执行快速一次性初始化,尽可能减少开销,用户无需显式调用。
- 当前
static
构造函数方法的一个特定难题是,运行时必须对类型与静态构造函数的使用进行额外的检查,以确定是否需要运行静态构造函数。 这增加了可衡量的开销。 - 使源生成器能够运行某些全局初始化逻辑,而无需用户显式调用任何内容
详细设计
通过使用 [ModuleInitializer]
属性修饰方法,可以将方法指定为模块初始值设定项。
using System;
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class ModuleInitializerAttribute : Attribute { }
}
可以使用该属性,如下所示:
using System.Runtime.CompilerServices;
class C
{
[ModuleInitializer]
internal static void M1()
{
// ...
}
}
对以此属性为目标的方法施加了一些要求:
- 该方法必须是
static
。 - 该方法必须是无参数的。
- 该方法必须返回
void
。 - 该方法不得为泛型或包含在泛型类型中。
- 必须可从包含模块访问该方法。
- 这意味着该方法的有效访问性必须是
internal
或public
。 - 这也意味着该方法不能是本地函数。
- 这意味着该方法的有效访问性必须是
在编译中找到具有此属性的一个或多个有效方法时,编译器将发出调用每个特性化方法的模块初始值设定项。 调用将以保留但确定的顺序发出。
缺点
为什么我们不这样做?
- 也许现有的用于“注入”模块初始值设定项的第三方工具足以满足要求此功能的用户。