带有可触发初始化的c#单例模式
本文关键字:单例模式 初始化 | 更新日期: 2023-09-27 18:04:25
我需要一个单例:
- 是惰性加载
- 线程安全
- 在construction中加载一些值
- 这些值可以随时查询
- 初始化可能发生在某个精确的时间,在查询开始之前-所以我必须能够从外部触发它。当然,多次触发应该只初始化一次。
我使用。net 3.5。
我从Jon Skeet使用静态子类的实现(第5版)开始:
public sealed class Singleton
{
IEnumerable<string> Values {get; private set;}
private Singleton()
{
Values = new[]{"quick", "brown", "fox"};
}
public static Singleton Instance { get { return Nested.instance; } }
private class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
这几乎勾起了所有的框,除了"从外部触发初始化"。因为实际的初始化是在actor内部进行的,所以只能进行一次。
如何做到这一点?
单例模式将像这样使用:
public static void Main(){
//do stuff, singleton should not yet be initialized.
//the time comes to initialize the singleton, e.g. a database connection is available
//this may be called 0 or more times, possibly on different threads
Singleton.Initialize();
Singleton.Initialize();
Singleton.Initialize();
//actual call to get retrieved values, should work
var retrieveVals = Singleton.Instance.Values;
}
似乎你可以这样做:
public sealed class Singleton
{
IEnumerable<string> Values {get; private set;}
private Singleton(bool loadDefaults)
{
if (loadDefaults)
Values = new[]{"quick", "brown", "fox"};
else
Values = new[]{"another", "set", "of", "values"};
}
public static Singleton Instance { get { return Nested.instance; } }
public static void Initialize() {
Nested.Initialize();
}
private class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly Singleton instance = new Singleton(true);
private static object instanceLock = new object();
private static bool isInitialized = false;
public static void Initialize() {
lock(instanceLock) {
if (!isInitialized) {
isInitialized = true;
instance = new Singleton(false);
}
}
}
}
}
或者创建将被更新的单个实例:
public sealed class Singleton
{
IEnumerable<string> Values {get; private set;}
private Singleton()
{
Values = new[]{"quick", "brown", "fox"};
}
public static Singleton Instance { get { return Nested.instance; } }
private static object instanceLock = new object();
private static bool isInitialized = false;
public static void Initialize() {
lock(instanceLock) {
if (!isInitialized) {
isInitialized = true;
Instance.Values = new[]{"another", "set", "of", "values"};
}
}
}
private class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
基于不可变注释和移除嵌套类注释的第三种变体:
public sealed class Singleton
{
IEnumerable<string> Values {get; private set;}
private Singleton()
{
Values = new[]{"quick", "brown", "fox"};
}
private static Singleton instance;
private static object instanceLock = new object();
public static Singleton Instance {
get {
Initialize();
return instance;
}
}
public static void Initialize() {
if (instance == null) {
lock(instanceLock) {
if (instance == null)
instance = new Singleton();
}
}
}
}
我的第一个想法是使用分配给单例实例的一次性变量,这将(可能?)触发初始化
static Main()
{
var unused = Singleton.Instance;
//this should initialize the singleton, unless the compiler optimizes it out.
//I wonder if the compiler is smart enough to see this call has side effects.
var vals = Singleton.Instance.Values;
}
…但是副作用编程是我努力避免的,所以让我们把目的讲得更清楚一些。
public class Singleton {
public static void Initialize() {
//this accesses the static field of the inner class which triggers the private Singleton() ctor.
Instance._Initialize();
}
private void _Initialize()
{ //do nothing
}
[the rest as before]
}
所以用法是:
static Main()
{
//still wondering if the compiler might optimize this call out
Singleton.Initialize();
var vals = Singleton.Instance.Values;
}
顺便说一句,这个也可以:
static Main()
{
var vals = Singleton.Instance.Values;
}
除了编译器优化之外,我认为这处理了所有的要求。
如果需要稍后进行初始化,可以设置一个Initialize方法,该方法可以从外部触发,但如果每次触发的值都不同,那么它就不能是静态的,这违反了Singleton模式。
根据你的例子,它没有变量,我假设你只是延迟初始化发生(例程而不是构造函数),但你的问题表明你想要不同的值,但如果多个初始化发生在一起,它只初始化一次,所以我对此有点困惑。
我不确定您是否需要单例实现,但如果没有关于Initialize()是否每次运行相同的代码或具有某种类型的变量性质的信息,则无法完全回答
可以使用双重检查锁定模式。只需在Singleton类中添加以下代码:
public sealed class Singleton
{
..........................
private static object locker = new object();
private static bool initialized = false;
public static void Initialize() {
if (!initialized){
lock(locker) {
if (!initialized){
//write initialization logic here
initialized = true;
}
}
}
}
.......................
}
你可以这样做
public sealed class Singleton
{
IEnumerable<string> Values { get; set; }
private Singleton()
{
Console.WriteLine("-- Private Singleton constructor");
Values = new[] { "quick", "brown", "fox" };
}
public static Singleton Instance
{
get
{
Console.WriteLine("- Singleton Instance");
return Nested.instance;
}
}
public static void Initialize()
{
Console.WriteLine("- Singleton Initialize");
Nested.Initialize();
}
internal class Nested
{
private static object syncRoot = new object();
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
Console.WriteLine("-- Static Nested constructor");
}
internal static readonly Singleton instance = new Singleton();
internal static void Initialize()
{
lock (syncRoot)
{
Console.WriteLine("-- Locked");
Console.WriteLine("--- Nested Initialize");
Console.WriteLine("-- Unlocked");
}
}
}
}
使用class Program
{
static void Main(string[] args)
{
var i = Singleton.Instance;
i = Singleton.Instance;
Console.WriteLine("-----");
Singleton.Initialize();
Singleton.Initialize();
Singleton.Initialize();
Console.Read();
}
}
输出- Singleton Instance
-- Private Singleton constructor
-- Static Nested constructor
- Singleton Instance
-----
- Singleton Initialize
-- Locked
--- Nested Initialize
-- Unlocked
- Singleton Initialize
-- Locked
--- Nested Initialize
-- Unlocked
- Singleton Initialize
-- Locked
--- Nested Initialize
-- Unlocked
public class Singleton<T> where T : class, new()
{
private static T instance;
public static T Instance
{
get
{
if (instance == null)
{
throw new Exception("singleton needs to be initialised before use");
}
return instance;
}
}
public static void Initialise(Action<T> initialisationAction)
{
lock(typeof(Singleton<T>))
{
if (instance != null)
{
return;
}
instance = new T();
initialisationAction(instance);
}
}
}