1 背景动机

关于模块或者程序集初始化工作一直是C#的一个痛点,微软内部外部都有大量的报告反应很多客户一直被这个问题困扰,这还不算没有统计上的客户。那么解决这个问题,还有基于什么样的考虑呢?

  • 在库加载的时候,能以最小的开销、无需用户显式调用任何接口,使客户做一些期望的和一次性的初始化。

  • 当前静态构造函数方法的一个最大的问题是运行时会对带有静态构造函数的类型做一些额外的检查。这是因为要决定静态构造函数是否需要被运行所必须的一步,但是这个又有着显著的开销影响。

  • 使源代码生成器在不需要用户显式调用一些东西的情况下能运行一些全局的初始化逻辑。

2 详细设计

C# 9.0将模块初始化器设计为一个Attribute,用这个Attribute来修饰进行模块初始化逻辑的方法,就实现了模块初始化功能。这个Attribute被命名为ModuleInitializerAttribute,具体定义如下:

using System;
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class ModuleInitializerAttribute : Attribute { }
}

如果要使用模块初始化器,你只要将ModuleInitializerAttribute用在符合下面要求的方法上就可以了。

  1. 该方法必须使静态的、无参的、返回值为void的函数。

  2. 该方法不能是泛型或者包含在泛型类型里

  3. 该方法必须是可从其所在模块里访问的。也就是说,方法的有效访问符必须是internal或者public,不能是局部方法。

using System.Runtime.CompilerServices;
class MyClass
{
[ModuleInitializer]
internal static void Initializer()
{
// ...
}
}

被修饰为ModuleInitializerAttribute的静态方法会被编译器在编译时,在全局的静态构造函数中生成此代码调用。如果有多个被修饰为初始化器的函数,则每个函数生成一个初始化器代码调用,这些初始化器代码调用代码会按照一定的顺序(类型名称顺序和代码顺序)生成。当模块在被加载时,全局静态构造函数开始执行,从而完成模块代码初始化工作。

3 问题与最佳实践

模块初始化器与静态构造函数之间有着一定的关联影响。因为模块初始化器是一个静态方法,因而其被调用执行前,必然会引起其所处类型的静态构造函数的执行。请参考下列示例:

static class ModuleInit
{
static ModuleInit()
{
//先执行
Console.WriteLine("ModuleInit静态构造函数 cctor");
} [ModuleInitializer]
internal static void Initializer()
{
//在静态构造函数执行后才执行
Console.WriteLine("模块初始化器");
}
}

在一个模块中指定多个模块初始化器的时候,他们之间的顺序也是一个值得注意的问题。以上这些问题的存在,就要求我们注意以下几点:

  • 在指定了模块初始化器的类型中,不要在静态构造函数中,写与模块初始化器中代码有着顺序依赖代码,最好的就是不要使用静态构造函数。

  • 多个模块初始化器之间的代码,也不要有任何依赖关系,保持各个初始化器代码的独立性。

4 结束语]

日常开发中,我们通常需要在模块初始化的时候,做一些前置性的准备工作,以前常采用静态构造函数这种不具有全局性方法,局限性很大,现在,这些都得到了完美解决。

如对您有价值,请推荐,您的鼓励是我继续的动力,在此万分感谢。关注本人公众号“码客风云”,享第一时间阅读最新文章。

C# 9.0新特性详解系列之三:模块初始化器的更多相关文章

  1. C# 9.0新特性详解系列之五:记录(record)和with表达式

    1 背景与动机 传统面向对象编程的核心思想是一个对象有着唯一标识,表现为对象引用,封装着随时可变的属性状态,如果你改变了一个属性的状态,这个对象还是原来那个对象,就是对象引用没有因为状态的改变而改变, ...

  2. C#9.0新特性详解系列之六:增强的模式匹配

    自C#7.0以来,模式匹配就作为C#的一项重要的新特性在不断地演化,这个借鉴于其小弟F#的函数式编程的概念,使得C#的本领越来越多,C#9.0就对模式匹配这一功能做了进一步的增强. 为了更为深入和全面 ...

  3. C# 9.0新特性详解系列之一:只初始化设置器(init only setter)

    1.背景与动机 自C#1.0版本以来,我们要定义一个不可变数据类型的基本做法就是:先声明字段为readonly,再声明只包含get访问器的属性.例子如下: struct Point { public ...

  4. C# 9.0新特性详解系列之二:扩展方法GetEnumerator支持foreach循环

    1.介绍 我们知道,我们要使一个类型支持foreach循环,就需要这个类型满足下面条件之一: 该类型实例如果实现了下列接口中的其中之一: System.Collections.IEnumerable ...

  5. [转]Servlet 3.0 新特性详解

    原文地址:http://blog.csdn.net/xiazdong/article/details/7208316 Servlet 3.0 新特性概览 1.Servlet.Filter.Listen ...

  6. Servlet 3.0 新特性详解

    转自:http://www.ibm.com/developerworks/cn/java/j-lo-servlet30/#major3 Servlet 是 Java EE 规范体系的重要组成部分,也是 ...

  7. 【转帖】Servlet 3.0 新特性详解

    http://www.ibm.com/developerworks/cn/java/j-lo-servlet30/ Servlet 3.0 新特性概述 Servlet 3.0 作为 Java EE 6 ...

  8. Servlet 3.0 新特性详解 (转载)

    原文地址:https://www.ibm.com/developerworks/cn/java/j-lo-servlet30/ Servlet 3.0 新特性概述 Servlet 3.0 作为 Jav ...

  9. Android6.0 新特性详解

    一 运行时权限 Android6.0 引入了一个新的应用权限模型,期望对用户更容易理解,更易用和更安全.该模型将标记为危险的权限从安装时权限(Install Time Permission)模型 移动 ...

随机推荐

  1. 【Luogu】P2292 [HNOI2004]L语言 题解

    前置芝士:\(Trie\)字典树 这道题,说是AC自动机,实际上一个\(Trie+\)队列轻松搞定. 首先,我们对所有单词建一棵\(Trie\). 然后,定义一个空队列\(Q\),初始时把\(-1\) ...

  2. linux修改ssh远程端口22

    建议先查看redhat的release版本,CentOS 7的启动服务不同: # more /etc/redhat-release 正文: 如题: 在此前,建议先查看redhat的release版本, ...

  3. Docker(3)- Centos 7.x 下 Docker 镜像加速配置

    如果你还想从头学起 Docker,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1870863.html 前言 默认情况下,Docker ...

  4. CF777E Hanoi Factory

    DP单调栈优化 看到这道题可以很自然的想到DP 设$dp[i]$表示最后一个$ring$为$i$的最大高度 首先将$b$为第一关键字,$a$为第二关键字,升序排序元素 那么对于$i$来说,它下面的$r ...

  5. (3)ASP.NET Core3.1 Ocelot认证

    1.认证 当客户端通过Ocelot访问下游服务的时候,为了保护下游资源服务器会进行认证鉴权,这时候需要在Ocelot添加认证服务.添加认证服务后,随后使用Ocelot基于声明的任何功能,例如授权或使用 ...

  6. linux下的终端利器----tmux

    转:tmux 是指通过一个终端登录远程主机并运行后,在其中可以开启多个控制台的终端复用软件.类似GNU Screen,但来自于OpenBSD,采用BSD授权.使用它最直观的好处就是,通过一个终端登录远 ...

  7. 什么是麒麟(kylin)?查数据贼快的哟

    前言 微信搜[Java3y]关注这个有梦想的男人,点赞关注是对我最大的支持! 文本已收录至我的GitHub:https://github.com/ZhongFuCheng3y/3y,有300多篇原创文 ...

  8. Docker学习—Machine

    前言 前面<Docker学习-Compose>文中介绍了Compose的使用方式:接下来继续了解docker三剑客之一的 Machine: 一.Docker Machine简介 1.什么是 ...

  9. javascript中什么是函数

    函数的定义 在javascript中函数是一段可以被执行或调用任意次数的JavasScript代码,在数据类型中属于"function".函数也拥有属性和方法,因此函数也是对象. ...

  10. InnoDB Insert Buffer(插入缓冲 转)

    一,插入缓冲(Insert Buffer/Change Buffer):提升插入性能 只对于非聚集索引(非唯一)的插入和更新有效,对于每一次的插入不是写到索引页中,而是先判断插入的非聚集索引页是否在缓 ...