关于单例与静态构造函数
◆两个单例的例子(来自博客园:http://www.cnblogs.com/csharp4/archive/2010/10/05/1842915.html)
例一:
public sealed class Singleton
{
// 在静态私有字段上声明单例
//定义了一个内联初始化的静态字段(即隐式创建静态构造函数),这样将导致JIT标记为BeforeFieldInit。
private static readonly Singleton instance = new Singleton();
// 私有构造函数,确保用户在外部不能实例化新的实例
private Singleton(){}
// 只读属性返回静态字段
public static Singleton Instance
{
get
{
return instance;
}
}
}
标记类为sealed是好的,可以防止被集成,然后在子类实例化,使用在静态私有字段上通过new的形式,来保证在该类第一次被调用的时候创建实例,是不错的方式,但有一点需要注意的是,C#其实并不保证实例创建的时机,因为C#规范只是在IL里标记该静态字段是BeforeFieldInit,也就是说静态字段可能在第一次被使用的时候创建,也可能你没使用了,它也帮你创建了,也就是周期更早,我们不能确定到底是什么创建的实例。
例二:
public class Singleton
{
// 因为下面声明了静态构造函数,所以在第一次访问该类之前,new Singleton()语句不会执行。
private static readonly Singleton _instance = new Singleton();
public static Singleton Instance
{
get { return _instance; }
}
private Singleton()
{
}
// 声明静态构造函数就是为了删除IL里的BeforeFieldInit标记
// 以区别例一中静态自动在使用之前被初始化
//显式定义了静态构造函数,这样将使JIT不会添加BeforeFieldInit标记,从而控制准确的执行时间。
static Singleton()
{
}
}
这种方式,其实是很不错的,因为他确实保证了是个延迟初始化的单例(通过加静态构造函数),但是该静态构造函数里没有东西哦,所以能有时候会引起误解,尤其是在codereview或者代码优化的时候,不熟悉的人可能直接帮你删除了这段代码,那就又回到了版本2了哦,所以还是需要注意的,不过如果你在这个时机正好有代码需要执行的话,那也不错。
(静态构造函数只在类第一次使用的时候初始化,不用的话不初始化的,不像有些单例,用不用都先初始化好。)
----------------------------------------------------------------------------------------------------------
◆关于静态构造函数的一点总结(来自:http://baike.baidu.com/view/2634573.htm):
在使用静态构造函数的时候应该注意以下几点:
1、静态构造函数既没有访问修饰符,也没有参数。
--因为是.NET调用的,所以像public和private等修饰符就没有意义了。
2、在创建第一个类实例或任何静态成员被引用时,.NET将自动调用静态构造函数来初始化类。
--也就是说我们无法直接调用静态构造函数,也不可能知道静态构造函数何时会被调用。
3、一个类只能有一个静态构造函数。
4、无参数的构造函数可以与静态构造函数共存。
--尽管参数列表相同,但一个属于类,一个属于实例,所以不会冲突。
5、最多只运行一次。
6、静态构造函数不可以被继承。
7、如果没有写静态构造函数,而类中包含带有初始值设定的静态成员,那么编译器会自动生成默认的静态构造函数。
----------------------------------------------------------------------------------------------------------
◆这是两个自己运行的实例:
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1
{
class S2
{
private static readonly S2 _s2 = new S2();
public static S2 s2
{
get
{
return _s2;
}
}
static S2()
{
Console.WriteLine("class test.");
}
private S2() { }
}
public sealed class S3
{
private static readonly S3 _s3 = new S3();
public static S3 s3
{
get
{
return _s3;
}
}
private S3() { }
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(S2.s2);
Console.WriteLine("----------------------------");
Console.WriteLine(S3.s3);
Console.ReadKey();
}
}
}
运行结果:
总结:因为静态字段在IL里标记为BeforeFieldInit,所以它可能在第一次被使用之前就已经初始化了(也就是说我们无法确定它初始化的时间);静态构造函数能够确保在使用类之前不会初始化静态字段,而只有在第一次访问类时才进行初始化。
-------------------------------------------------------------------------------------------------------------------------------------------------
补充:还有一种不用static readonly声明和静态构造函数的单例:
例三:用静态私有字段声明单例
public sealed class Singleton
{
private static Singleton instance;
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
这个版本的主要问题,就是线程安全的问题,当2个请求同时方式这个类的实例的时候,可以会在同一时间点上都创建一个实例,虽然一般不会出异常错误,但是起码不是我们谈论的只保证一个实例了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端