.NET Core中延迟单例另一种写法【.NET Core和.NET Framework的beforefieldinit差异】
1.BeforeFieldInit是什么
前段时间在反编译代码时无意间看到在类中有一个BeforeFieldInit特性,处于好奇的心态查了查这个特性,发现这是一个关于字段初始化时间的特性【提前初始化字段】,下面先来看一下这个特性在.net framework中的作用
class Foo
{
public static String x = GetStr("初始化 Foo 静态成员字段");
public static String GetStr(String str)
{
Console.WriteLine(str);
return str;
}
}
在上面Foo类中只定义了一个静态字段x和一个静态方法GetStr的方法,在这里需要关注的是静态字段x的初始化时机
static void Main(string[] args)
{
Console.WriteLine("Main方法开始");
Foo.GetStr("手动调用Foo.GetSring()方法");
String y = Foo.x;
}
在Main中简单的调用静态方法和静态字段,我们知道静态字段的赋值是在静态构造函数中进行的,那么输出顺序应该是 “Main方法开始”,”初始化Foo静态成员字段“,”手动调用Foo.GetString()方法“,但是真的是这样吗,答案是错的

可以看到静态成员字段的初始化是在最开始,那么为什么会这样呢,我们将代码反编译IL后会发现在类中具有一个beforefieldinit特性,
.class private auto ansi beforefieldinit BeoreFieldInitTest2.Foo
extends [mscorlib]System.Object
{
} // end of class BeoreFieldInitTest2.Foo
那么BeforeFieldInit是什么,我找到了一篇文章有对BeforeFieldInit的详细讲解,在这里也不过多介绍,
2.取消BeforeFieldInit加载
那么该怎么取消beforefieldinit特性呢,其实很简单,只需要在类中加入一个静态构造函数即可
class Foo
{
public static string x = GetStr("初始化 Foo 静态成员字段");
//空的静态构造函数
static Foo(){}
public static String GetStr(String str)
{
Console.WriteLine(str);
return str;
} }
然后此时输入就如我们所猜测那样

并且反编译可以看到IL代码也取消了beforefieldinit特性
.class private auto ansi BeoreFieldInitTest2.Foo
extends [mscorlib]System.Object
{
} // end of class BeoreFieldInitTest2.Foo
下面就该进入正题,来看看.NET Core中不一样的BeforeFieldInit
3.BeforeFieldInit在.NET Core 中的差异
将最开始的代码在.NET Core中跑一跑会发现跟.NET Framework不一样的操作
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main方法开始");
Foo.GetStr("手动调用Foo.GetSring()方法");
String y = Foo.x;
}
}
class Foo
{
public static string x = GetStr("初始化 Foo 静态成员字段");
public static String GetStr(String str)
{
Console.WriteLine(str);
return str;
}
}

可以看到在.NET Core并没有像.NET Framework那样进行提前加载,并且加载貌似还延迟了,反编译代码可以看到beforefieldinit特性还在Foo类上
.class private auto ansi beforefieldinit BeforeFieldInitTest.Foo
extends [System.Runtime]System.Object
{
} // end of class BeforeFieldInitTest.Foo
那么在.NET Core加入静态构造函数会怎么呢?怀着各种疑惑进行测试
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main方法开始");
Foo.GetStr("手动调用Foo.GetSring()方法");
String y = Foo.x;
}
}
class Foo
{
public static string x = GetStr("初始化 Foo 静态成员字段");
//空的静态构造函数
static Foo() { }
public static String GetStr(String str)
{
Console.WriteLine(str);
return str;
}
}

可以看到.NET Core中加入静态构造函数以后输出跟.NET Framework一致,也就说可以猜测.NET Core运行时对beforefieldinit特性进行了优化,当然这也只是我的猜测
4.利用.NET Core中beforefieldinit实现的单例
在.NET Framework中我们都是使用Lazy<>类来创建延迟加载单例,但是我们可以看到在.NET Core中beforefieldinit是延迟加载的,所以我们直接可以使用此方法来创建延迟安全单例,
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main方法开始");
Foo.GetStr("手动调用Foo.GetSring()方法");
Console.WriteLine("我是分隔符");
Console.WriteLine("我是分隔符");
var foo= Foo.CreateInstance;
}
}
class Foo
{
public static Foo CreateInstance { get; } = new Foo();
private Foo()
{
Console.WriteLine("创建了Foo实例");
}
public static String GetStr(String str)
{
Console.WriteLine(str);
return str;
}
}
运行结果可以看到创建实例被延迟了,

当然,这种创建单例也是有缺点的,当类中还有其它静态字段或属性时,并且在外部进行了调用,那么此时也会初始化此属性
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main方法开始");
Foo.GetStr("手动调用Foo.GetSring()方法");
var y = Foo.x;//调用静态字段/属性
Console.WriteLine("我是分隔符");
Console.WriteLine("我是分隔符");
var foo= Foo.CreateInstance;
}
}
class Foo
{
public static string x = GetStr("初始化 Foo 静态成员字段"); //加入了静态字段或属性
//public static String X { get; set; } = GetStr("初始化 Foo 静态成员字段");
public static Foo CreateInstance { get; } = new Foo();
private Foo()
{
Console.WriteLine("创建了Foo实例");
}
public static String GetStr(String str)
{
Console.WriteLine(str);
return str;
}
}

也就是说在.NET Core中beforfieldinit特性时当有一个静态变量被使用时就初始化所有静态变量
.NET Core中延迟单例另一种写法【.NET Core和.NET Framework的beforefieldinit差异】的更多相关文章
- Java基础系列-单例的7种写法
原创文章,转载请标注出处:https://www.cnblogs.com/V1haoge/p/10755322.html 一.概述 Java中单例有7种写法,这个是在面试中经常被问到的内容,而且有时候 ...
- Java并发编程中的设计模式解析(二)一个单例的七种写法
Java单例模式是最常见的设计模式之一,广泛应用于各种框架.中间件和应用开发中.单例模式实现起来比较简单,基本是每个Java工程师都能信手拈来的,本文将结合多线程.类的加载等知识,系统地介绍一下单例模 ...
- 「Android」单例的五种写法
单例 发现博客园可以很好的设置自己的博客文章的展示,很开心,然后特此发一篇 其实这几种写法大家应该都会的,就权当拿来记录一下吧,以后复习巩固也比较方便. 这篇文章中的代码,来自一篇视频(我想找视频贴上 ...
- java23种设计模式之二: 单例设计模式(6种写法)
目的:在某些业务场景中,我们需要某个类的实例对象的只能有一个,因此我们需要创建一些单例对象. 本文共有6种写法,仅供参考 1.饿汉式 优点: 在多线程情况下,该方法创建的单例是线程安全的(立即加载) ...
- java单例的几种写法
转载出处:http://cantellow.javaeye.com/blog/838473 第一种(懒汉,线程不安全): public class Singleton { private static ...
- iOS单例的两种实现
单例模式算是开发中比较常见的一种模式了.在iOS中,单例有两种实现方式(至少我目前只发现两种).根据线程安全的实现来区分,一种是使用@synchronized,另一种是使用GCD的dispatch_o ...
- 【cocos2d-js官方文档】二十五、Cocos2d-JS v3.0中的单例对象
为何将单例模式移除 在Cocos2d-JS v3.0之前.全部API差点儿都是从Cocos2d-x中移植过来的,这是Cocos2d生态圈统一性的重要一环.可惜的是,这样的统一性也在非常大程度上限制了C ...
- Spring5源码解析-Spring框架中的单例和原型bean
Spring5源码解析-Spring框架中的单例和原型bean 最近一直有问我单例和原型bean的一些原理性问题,这里就开一篇来说说的 通过Spring中的依赖注入极大方便了我们的开发.在xml通过& ...
- 在Swift中实现单例方法
在写Swift的单例方法之前可以温习一下Objective-C中单例的写法: + (instancetype)sharedSingleton{ static id instance; static d ...
随机推荐
- Linux扩展分区记录
Vmware中新建的Linux虚拟机,数据盘规划太小,数据量超出磁盘大小,本文介绍如何快速扩充空间 参考:https://blog.csdn.net/lyd135364/article/details ...
- Tomcat异常:server Tomcat v9.09 Server at localhost failed to start
详细报错: 首先不要慌张,这不是Tomcat引发的问题.而是你自己代码错误导致的问题(小编遇到的是配置servlet-mapping时,url-pattern中配置不合法) 然后,检查控制台打印信息, ...
- ThinkPhp框架对“数据库”的基本操作
框架有时会用到数据库的内容,在"ThinkPhp框架知识"的那篇随笔中提到过,现在这篇随笔详细的描述下. 数据库的操作,无疑就是连接数据库,然后对数据库中的表进行各种查询,然后就是 ...
- redis中各种数据类型的常用操作方法汇总
在spring中使用jedisTemplate操作,详见https://www.cnblogs.com/EasonJim/p/7803067.html 一.Redis的五大数据类型 1.String( ...
- Java判断一个字符是否是数字的几种方法的代码
在工作期间,将写内容过程经常用到的一些内容段做个记录,下面内容是关于Java判断一个字符是否是数字的几种方法的内容,希望能对码农们有好处. public class Test{ public stat ...
- 网站的增 / 删 / 改 / 查 时常用的 sql 语句
最近在学习数据库 php + mysql 的基本的 crud 的操作,记录碰到的坑供自己参考.crud中需要用到的sql语句还是比较多的,共包括以下几个内容: 查询所有数据 查询表中某个字段 查询并根 ...
- Android 7.0 fiddler代理抓不到https请求的解决办法
解决方法: 1.在源码res目录下新建xml目录,增加network_security_config.xml文件 (工程名/app/src/main/res/xml/network_security ...
- Linux中安装硬盘后对硬盘的分区以及挂载
我将使用VM来进行模拟 先使用df看下我的电脑硬盘信息: df -h 可以看到只有一个sda1分区装载/boot,还有一个扩展分区 查看dev下的硬盘: 只有一个硬盘(两个分区) 注意: 如果你是ID ...
- NoSQL与MongoDB介绍
写在前面 本文是由一次演讲整理出来的,文中大部分资料来源于网络,感谢Wikipedia,Google和MongoDB官网.文中使用的MongoDB版本为1.2.4. What is NoSQL NoS ...
- Android长时间定时任务实现
在服务的onStartCommand方法里面使用AlarmManager 定时唤醒发送广播,在广播里面启动服务 每次执行startService方法启动服务都会执行onStartCommand 1.服 ...