[C#]BeforeFieldInit与类静态构造函数

2023-11-20

      最近看到一篇关于 C# 语言中关于静态构造函数延迟加载核心原理的文章,感觉很好特转一下,以供朋友们共同进步! (个人很少转载他人文章)


如下代码:
 
using System;
namespace BeforeFieldInit
{
    internal class Foo
    {
        Foo(){ Console.WriteLine("Foo 对象构造函数");}
        public static string Field = GetString("初始化 Foo 静态成员变量!");

        public static string GetString(string s){
            Console.WriteLine(s);
            return s;
        }
    }

    internal class FooStatic
    {
        static FooStatic(){ Console.WriteLine("FooStatic 类构造函数"); }
        FooStatic(){ Console.WriteLine("FooStatic 对象构造函数"); }

        public static string Field = GetString("初始化 FooStatic 静态成员变量!");
        public static string GetString(string s){
            Console.WriteLine(s);
            return s;
        }
    }

    class Program
    {
       static void Main(string[] args){
            Console.WriteLine("Main 开始 ...");

            Foo.GetString("手动调用 Foo.GetString() 方法!");
            //string info = Foo.Field;

            FooStatic.GetString("手动调用 FooStatic.GetString() 方法!");
            //string infoStatic = FooStatic.Field;

            Console.ReadLine();
        }
    }
}

  Foo 和FooStatic 唯一的不同就是FooStatic 有静态的类构造函数。执行上面的代码,输出如下:

wKioL1j9bJmwWYkZAAAS97GYgKA327.png


  如果把被注释的读取静态字段Field的两行代码打开,再编译运行,输出:

wKioL1j9bKWQ-6HdAAAQQaV0zHQ888.png



对比上面的区别,FooStatic 始终是延迟装载的,也就是只有类被首次使用时,类对象才被构造,其静态成员以及静态构造函数才被初始化执行,而Foo 类对象的初始化则交给CLR 来决定。
如果用IL Dasm.exe对比两个类生成的中间代码,可以看到只有一处不同:FooStatic 比Foo 少了一个特性:

wKioL1jYeIbwEJmDAABdz1IunUs999.png


也就是说静态构造函数抑制了beforefieldinit 特性,而该特性会影响对调用该类的时机。
C# 里面的静态构造函数,也称为类型构造器,类型初始化器,它是私有的,就是在上图中的.cctor : void()。CLR保证一个静态构造函数在每个AppDomain中只执行一次,而且这种执行是线程安全的,所以在静态构造函数中非常适合于单例模式的初始化(初始化静态字段等同于在静态构造函数中初始化,但不完全相同,因为显式定义静态构造函数会抑制beforefieldinit标志。)。
JIT编译器在编译一个方法时,会查看代码中引用了哪些类型,任何一个类型定义了静态构造函数,JIT编译器都会检查针对当前AppDomain,是否执行了这个静态构造函数。如果类型构造去没有执行,JIT编译器就会在生成的本地代码中添加对静态构造函数的一个调用,否则就不会添加,因为类型已经初始化。同时CLR还保证在执行本地代码中生成的静态构造函代码的线程安全。
根据上面的描述,我们知道JIT 必须决定是否生成类型静态构造函数代码,还须决定何时调用它。具体在何时调用有两中方式:
precise:JIT编译器可以刚好在创建类型的第一个实例之前,或刚好在访问类的一个非继承的字段或成员之前生产这个调用。
beforefieldinit:JIT编译器可以在首次访问一个静态字段或者一个静态/实例方法之前,或者创建类型的第一个实例之前,随便找一个时间生成调用。具体调用时机由CLR决定,它只保证访问成员之前会执行静态构造函数,但可能会提前很早就执行。

 

CLI specification (ECMA 335) 在 8.9.5 节中提到:
1. If marked BeforeFieldInit then the type's initializer method is executed at, or sometime before, first access to any static field defined for that type
2. If not marked BeforeFieldInit then that type's initializer method is executed at (i.e., is triggered by):
? first access to any static or instance field of that type, or
? first invocation of any static, instance or virtual method of that type
简单点说就是beforefieldinit可能会提前调用一个类型的静态构造函数,而precise模式是非要等到用时才调用类型的静态构造函数,它是严格的延迟装载。
beforefieldinit 是首选的(如果没有自定义静态构造函数,默认就是这种方式),因为它使CLR能够自由选择调用静态构造函数的时机,而CLR会尽可能利用这一点来生成运行得更快的代码。比如说在一个循环中调用单例(且包含首次调用),beforefieldinit方式可以让CLR决定在循环之前就调用静态构造函数来优化,而precise模式则只会在循环体中来调用静态构造函数,并在之后的调用会检测静态构造函数是否已被执行的标志位,这样效率稍低一些。在前面使用静态Field的情况下,beforefieldinit 方式下CLR也认为提前执行静态构造函数是更好的选择。
C# 的单例实现,可以利用 precise 延迟调用这一点来延迟对单例对象的构造(饿汗模式),从而带来一丁点的优化,但是在绝大部分情况下这一丁点的优化作用并不大!


转载于:https://blog.51cto.com/liuguozhu/1910626

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[C#]BeforeFieldInit与类静态构造函数 的相关文章

  • json.net自定义jobject反序列化

    我正在尝试使用 JsonConvert DeserializeObject string 将字符串反序列化为可与动态一起使用的 jobject 来动态访问 json 文档 但是我想避免知道文档的大小写 以便我可以输入 dynamic doc
  • 将 new 与 decltype 一起使用

    T t T is an implementation detail t new T want to avoid naming T to allow for flexibility t new decltype t error cannot
  • Poco c++Net:Http 从响应中获取标头

    我使用 POCO C Net 库进行 http 我想尝试制定持久缓存策略 首先 我认为我需要从缓存标头中获取过期时间 并与缓存值进行交叉检查 如果我错了 请告诉我 那么我如何从中提取缓存头httpResponse 我已经看到你可以用 Jav
  • 在现代 C++ 中,临时生命周期延长何时有用?

    在 C 中 您可以将函数的返回值 返回值 而不是引用 绑定到 const 引用 并且代码仍然有效 因为该临时对象的生命周期将延长到作用域末尾 例如 std string get string return abc void f const
  • 在 C# 中调用 C++ 库 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我有很多用 C 编写的库 我想从 C 调用这些库 但是 我遇到了很多问题 我想知道是否有书籍或指南告诉我如何做到这一点 Dll导入 htt
  • 运行需要 MySql.Data 的内置 .NET 应用程序

    我在运行我编写的内置 NET 应用程序时遇到问题 我的应用程序使用最新的 MySql 连接器 该连接器安装在我的系统上 当我尝试将其添加为引用时 该连接器显示为 NET 4 Framwork 组件 当我在环境中以调试模式运行应用程序时 一切
  • 检测到堆栈崩溃

    我正在执行我的 a out 文件 执行后 程序运行一段时间 然后退出并显示消息 stack smashing detected a out terminated Backtrace lib tls i686 cmov libc so 6 f
  • 如何制作可启动程序?

    所以 这个问题可能看起来很奇怪 但假设我编译了 int main void int x 3 int y 4 int z x y 是否可以让CPU这样运行 如何 例如 这允许我写入监视器吗 如果我没记错的话 内存中有些地方可以写入要显示的内容
  • C# 5 async/await 线程机制感觉不对?

    为什么让调用线程进入异步方法直到内部 等待 一旦调用异步方法就生成一个线程 这不是更干净吗 这样您就可以确定异步方法会立即返回 您不必担心在异步方法的早期阶段没有做任何昂贵的事情 我倾向于知道某个方法是否要在 我的 线程上执行代码 不管是堵
  • 计算另一个表达式中的 C# 表达式

    我想在另一个表达式中使用一个表达式 Expression
  • 为什么'enable_if'不能用于禁用这里声明

    include
  • 在 omp 并行 for 循环中使用 unique_ptr 会导致 SEG.FAULT

    采取以下代码 include
  • 增强精神、递归和堆栈溢出

    为什么下面的代码在运行时崩溃 它会给出堆栈溢出错误 include
  • 根据对象变量搜索对象列表

    我有一个对象列表 这些对象具有三个变量 ID 名称和值 这个列表中可能有很多对象 我需要根据ID或Name找到一个对象 并更改值 例子 class objec public string Name public int UID public
  • 析构函数中的异步操作

    尝试在类析构函数中运行异步操作失败 这是代码 public class Executor public static void Main var c1 new Class1 c1 DoSomething public class Class
  • 使用具有抗锯齿功能的 C# 更改抗锯齿图像的背景颜色

    我有一个图像需要更改背景颜色 例如 将下面示例图像的背景更改为蓝色 然而 图像是抗锯齿的 所以我不能简单地用不同的颜色替换背景颜色 我尝试过的一种方法是创建第二个图像 仅作为背景 并更改其颜色并将两个图像合并为一个图像 但是这不起作用 因为
  • 如何使用 NPOI 按地址(A1、A2)获取 Excel 单元格值

    我有一个 Excel 单元格地址 例如 A1 A2 如何使用 C 中的 NPOI 框架以编程方式访问此单元格 我找到的一些 Java POI 示例代码 CellReference cr new CellReference A1 row my
  • 从BackgroundWorker线程更新图像UI属性

    在我正在编写的 WPF 应用程序中 我有一个 TransformedBitmap 属性 该属性绑定到 UI 上的 Image 对象 每当我更改此属性时 图像就会更新 因此显示在屏幕上的图像也会更新 为了防止在检索下一张图像时 UI 冻结或变
  • 如何从 Rx Subscribe 回调异步函数?

    我想回调 Rx 订阅中的异步函数 例如 像那样 public class Consumer private readonly Service service new Service public ReplaySubject
  • ASP.NET MVC 路由:如何从 URL 中省略“索引”

    我有一个名为 StuffController 的控制器 具有无参数索引操作 我希望从表单中的 URL 调用此操作mysite com stuff 我的控制器定义为 public class StuffController BaseContr

随机推荐

  • 动手学深度学习——softmax回归(原理解释+代码详解)

    目录 1 softmax回归 1 1 分类问题 1 2 网络架构 1 3 全连接层的参数开销 1 4 softmax运算 1 5 小批量样本的矢量化 1 6 损失函数 1 6 1 对数似然 1 6 2 softmax及其导数 1 6 3 交
  • 算法导论 学习笔记 第七章 快速排序

    快排最坏时间复杂度为 n 但它的平均性能很好 通常是实际排序应用中最好的选择 它的期望时间复杂度为 nlgn 且 nlgn 中隐含的常数因子非常小 且它还能进行原址排序 快排也使用了分治思想 1 分解 数组被划分为两个子数组 使得一个子数组
  • 服务器如何开多个虚拟机,服务器运行多个虚拟机

    服务器运行多个虚拟机 内容精选 换一换 通过内网连接云手机实例时 需要在租户VPC中创建一台弹性云服务器 作为连接云手机的跳板机器 若创建云手机服务器时未使用自定义网络 还需在云手机租户的VPC和服务器所在VPC之间建立对等连接 如图1所示
  • SpringBoot集成Netty时在Handler类中注入service和dao为null

    最近在做一个服务器接收客户端消息 之前一直都只接触web开发 第一次接触服务器开发 接触到Netty 但在Netty的Handler类里注入为null public class HttpRequestHandler extends Simp
  • 【vue3练习 -12】vue3使用readonly(),shallowReadonly()

    作用 把一个响应式 可以是ref定义的 也可以是reactive定义的 的数据变成只读的 不可以修改 使用场景 假如你的组件有个数据 但是你不希望在使用的时候修改他就可以把他变成只读的 用法示例 import readonly shallo
  • SOLO算法解读

    论文 SOLO Segmenting Objects by Locations 论文链接 https arxiv org abs 1912 04488 代码链接 GitHub WXinlong SOLO SOLO and SOLOv2 fo
  • 安全之安全(security²)博客目录导读

    研究方向 安全之安全 研究内容 ARM RISC V安全架构 TF A TEE之安全 GP安全认证 静态代码分析 FUZZ模糊测试 IDA逆向分析 安全与功耗等 欢迎您的关注 一 ARM安全架构 1 ARM安全架构及其发展趋势 转载 2 A
  • uc同步登陆同步退出

    几乎每个应用在整合UC的时候都会遇到无法同步登陆同步退出的情况 今天分析下原因 首先我们的项目会将uc client这个文件夹原封不动的拷贝到项目根目录 public function inteLogin loginname passwor
  • 从零开始学Python(一)基本变量与数据类型

    Welcome Huihui s Code World 接下来看看由辉辉所写的关于Python的相关操作吧 目录 Welcome Huihui s Code World 一 关于Python的基本知识 变量 注释 二 数据类型 1 强类型
  • 蝴蝶效应,青蛙现象,鳄鱼法则,鲇鱼效应,羊群效...

    标题 蝴蝶效应 青蛙现象 鳄鱼法则 鲇鱼效应 羊群效应 刺猬法则 手表定律 破窗理论 二八定律 木桶理论 马太效应 这些你都明白吗 1 蝴蝶效应 上个世纪70年代 美国一个名叫洛伦兹的气象学家在解释空气系统理论时说 亚马逊雨林一只蝴蝶翅膀偶
  • pycharm快捷键及一些常用设置

    Alt Enter 自动添加包 shift O 自动建议代码补全 Ctrl t SVN更新 Ctrl k SVN提交 Ctrl 注释 取消注释 选择的行 Ctrl Shift F 高级查找 Ctrl Enter 补全 Shift Enter
  • 安装ThinkPHP5.1并在框架中使用FFmpeg视频处理工具遇到的问题和解决办法

    一 安装ThinkPHP5 1框架 问题一 安装方法有很多 我这里使用composer安装的 但是遇到了问题 出现了报错 安装方法可是查看 https www kancloud cn manual thinkphp5 1 353948 co
  • FreeRTOS中断管理

    目录 说明 一 中断基础 1 1 中断理解 1 2 中断执行步骤 1 3 中断寄存器选择位 1 4 中断优先级分类 二 中断优先级分组设置 2 1 分类 2 2 特点 三 中断有关寄存器 3 1 SHPR1寄存器 3 2 SHPR2寄存器
  • 【区块链与密码学】第2-3讲:区块链基础技术大剖析之哈希函数

    本课堂内容全部选编自PlatON首席密码学家 武汉大学国家网络安全学院教授 博士生导师何德彪教授的 区块链与密码学 授课讲义 教材及互联网 版权归属其原作者所有 如有侵权请立即与我们联系 我们将及时处理 2 4 1 哈希函数 区块链作为一个
  • gateway集成阿里Sentinel后,控制台如何根据url动态限流

    gateway网关集成Sentinel后 控制台默认限流是根据服务进行的 默认为资源名为ReactiveCompositeDiscoveryClient 服务名称 如下图 所以限流都是根据整个服务进行的 那么如何像单机一样根据url分组限流
  • Git常见问题:Your branch and ‘xxx/xxx‘ have diverged

    报这个错就是因为你和远程库出现分叉了 具体表现是是由于同一个分支有多人修改 多人commit造成的 用户1操作 用户2操作 T1 git co b brv 1 git co b brv 1 T2 git add test2 java git
  • stm32的HAL库函数flash无法写入的问题

    记录一下对flash编程操作时出现的问题以及解决办法 问题 在使用HAL库中的HAL FLASH Program uint32 t TypeProgram uint32 t Address uint64 t Data 函数时发现总是没办法写
  • 从阿里云OSS获取文件,下载打包

    从OSS服务上批量获取文件下载 并打包zip Slf4j Component RefreshScope public class OssBootUtil 初始化 oss 客户端 return private static OSSClient
  • Common块和Bss段的区别

    昨天看 程序员的自我修养 链接 装载与库 发现不是很理解为什么要用common块 然后仔细看了一番 有了自己的理解 common块 用来存放弱符号 而全局未初始化变量是弱符号 但是难道不是应该存放在 bss段吗 为什么要有common块呢
  • [C#]BeforeFieldInit与类静态构造函数

    最近看到一篇关于 C 语言中关于静态构造函数延迟加载核心原理的文章 感觉很好特转一下 以供朋友们共同进步 个人很少转载他人文章 如下代码 using System namespace BeforeFieldInit internal cla