一.前言
单例模式,简单字面意思就是整个程序中只进行一次初始化的操作。相对于静态类,单例模式能做到延迟加载,以及类继承。
目前有6+种实现方式。本文只记录“静态构造函数下的单例模式”和“Initialization Demand Holder(IoDH)”。其他部分在文末有转载链接可以查看。
二.代码记录
2.1 静态构造函数实现单例模式
2.1.1 执行记录
这里我们直接上代码,根据结果进行记录:
字段x初始化会在构造函数前面先执行。(重点。后面单例模式里面执行代码和想象中不同,考虑是这个原因。)
public class SingletonTest
{
public static string x = EchoAndReturn("A_In type initializer");
/// <summary>
/// 当没有写静态构造函数时,框架会自动生成
/// 导致静态字段的初始化跑到了静态方法调用之前
/// 造成对象的提前初始化
/// </summary>
static SingletonTest()
{
x = "A_static";
Console.WriteLine("A_static init");
}
public SingletonTest()
{
x = "A_nonstatic";
Console.WriteLine("A_nonstatic init");
}
public static string EchoAndReturn(string s)
{
Console.WriteLine(s);
return s;
}
}
对应main函数如下:
static void Main(string[] args)
{
Console.WriteLine("Starting Main");
//SingletonTest_inherit b = new SingletonTest_inherit();
SingletonTest.EchoAndReturn("Echo!");
Console.WriteLine("After echo");
string y = SingletonTest.x;
if (!string.IsNullOrEmpty(y))
{
Console.WriteLine(y);
}
//y = SingletonTest_inherit.x;
//if (!string.IsNullOrEmpty(y))
//{
// Console.WriteLine(y);
//}
Console.ReadKey();
}
可以看到:
1.程序运行--》2.执行main函数--》3.使用到静态SingletonTest--》4.静态字段x的初始化--》5.静态构造函数的初始化--》6.静态函数EchoAndReturn的实现
--》字段x的值由于执行顺序,不是默认赋值的"A_In type initializer",而是静态构造函数中赋值的x = "A_static";
如果这里不加静态构造函数?
1.程序运行--》2.静态类型的初始化,静态SingletonTest、字段x提前初始化--》3.执行main函数。
这里在文末的博文中提到是:
在类中实现静态构造函数,那beforefieldinit属性就会被precise属性替换,确保静态成员会在类第一次使用之前的那一刻进行初始化。如果不显式实现,静态成员会在类第一次使用之前的任何时间初始化(由CLR智能决定)。
比较得出:
显式静态构造函数使对象在被调用的时候才初始化,避免了static类型在程序启动的时候就提前初始化的问题。有利于程序的快速启动与内存的控制。
2.1.2 单例模式实现
public class SingletonTest
{
private static readonly SingletonTest _instance = new SingletonTest();
static SingletonTest()
{
}
private SingletonTest()
{
}
public static SingletonTest Instance
{
get
{
return _instance;
}
}
}
1.将构造函数private;
2.显式实现static构造函数;
3.创建私有静态实例_instance并初始化;
4.public开放实例对外访问接口 Instance 属性。
这样避免了静态类型的提前初始化,同时直接在初始化时候赋值的方式也避免了需要加锁的问题。
2.1.3 单例模式执行顺序
public class SingletonTest
{
private static readonly SingletonTest _instance = new SingletonTest();
static SingletonTest()
{
Console.WriteLine("static init console");
x = "static init";
}
private SingletonTest()
{
Console.WriteLine("nonstatic init console");
x = "nonstatic init";
}
public static SingletonTest Instance
{
get
{
Console.WriteLine("Instance");
return _instance;
}
}
//测试静态字段,对执行顺序无影响,实际使用应该为非static,同时操作应该通过XX,不能直接操作本字段
private static string x = "xxxx";
public string XX
{
get { return x; }
private set { x = value; }
}
}
main函数中输出 Console.WriteLine( SingletonTest.Instance.XX);
执行结果:
1.程序启动--》2.遇到类SingletonTest--》3.优先初始化static属性_instance(Instance没有set,显式写也不会执行)--》4._instance=new SingletonTest()进行实例化--》5.完成static属性的初始化后进行static构造函数的初始化--》6.调用SingletonTest.Instance--》7.获取结果XX得"static init"
--》注意这里不是先static init,然后才nonstatic init。因为属性的初始化优先于static构造函数。所以最后的结果是静态构造函数得执行顺序在nonstatic的后面。
2.1.4 继承问题
这里题外话记录一下继承时的表现,进一步展示函数执行顺序:
增加子类型:
public class SingletonTest_inherit : SingletonTest
{
static SingletonTest_inherit()
{
x = "B_static";
Console.WriteLine("B_static init");
}
public SingletonTest_inherit()
{
x = "B_nonstatic";
Console.WriteLine("B_nonstatic init");
}
}
执行结果:
子对象的静态构造函数没有执行。
如果这里增加实例化?
1.父类型字段x初始化--》2.父类型static构造函数执行--》3.子类型static构造函数执行--》4.父类型构造函数执行--》5.子类型构造函数执行
--》这里父类型和子类型共用同一个字段x,导致字段x的赋值被子类型重写。
2.2 IoDH实现
2.2.1单例模式实现
推荐单例模式使用该方式。在类内部定义内部类来实现单例模式。
public class SingletonTest
{
private class InnerCLass
{
static InnerCLass() { }
internal static SingletonTest instance = new SingletonTest();
}
private SingletonTest() { }
public static SingletonTest Instance
{
get { return InnerCLass.instance; }
}
}
2.1.3 单例模式执行顺序
public class SingletonTest
{
private class InnerCLass
{
static InnerCLass()
{
Console.WriteLine("InnerCLass static");
}
internal static SingletonTest instance = new SingletonTest();
}
private SingletonTest() {
Console.WriteLine("SingletonTest nonstatic");
}
public static SingletonTest Instance
{
get {
Console.WriteLine("Instance");
return InnerCLass.instance; }
}
public string XX = "aaa";
}
三.相关参考链接
https://www.cnblogs.com/rush/archive/2011/10/30/2229565.html ---主要参考博文
https://www.cnblogs.com/jiagoushi/p/3775046.html ---提供继承方面的探讨
https://blog.csdn.net/abc524061/article/details/57086267?utm_source=itdadao&utm_medium=referral ---对第一个博文进行扩展和实践