具有可触发初始化的C#Singleton模式

时间:2011-08-04 14:32:58

标签: c# .net singleton initialization

我需要一个单身:

  • 延迟加载
  • 是线程安全的
  • 在施工中加载一些值
  • 可以随时查询这些值
  • 初始化可能在查询开始之前的某个精确时间发生 - 所以我必须能够以某种方式从外部触发它。当然,多次触发应该只进行一次初始化。

我使用.NET 3.5。

我使用静态子类开始使用Jon Skeet的implementation(第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();
    }
} 

除了“从外部触发初始化”外,几乎所有方框都会打勾。由于实际初始化发生在ctor内部,因此不会发生多次。

如何实现这一目标?

单身人士会像这样使用:

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;

}

6 个答案:

答案 0 :(得分:3)

好像你可以这样做:

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();
            }
        }
    }
} 

答案 1 :(得分:1)

我的第一个想法就是使用分配给单例实例的一次性变量,这可能(可能是?)触发初始化

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;
}

除了编译器优化之外,我认为这可以满足所有要求。

答案 2 :(得分:0)

你可以设置一个可以从外部触发的Initialize方法,如果你需要稍后进行初始化,但是如果每次触发时值都不同,那么它就不能是静态的,这违反了Singleton模式

根据你的例子,它没有变量,我假设你只是在初始化发生时延迟(例程而不是构造函数),但你的问题表明你想要不同的值,但如果多个初始化发生在一起,它只会初始化曾经,所以我对此感到有点困惑。

我不确定你是否只需要Singleton implmentation,但如果没有关于Initialize()每次是否运行相同的代码或具有某种类型的变量性质的信息,则无法完全回答。

答案 3 :(得分:0)

您可以使用双重检查锁定模式。只需在您的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;
                 }
              }
            }
        }

.......................

}

答案 4 :(得分:0)

你可以做这样的事情

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

答案 5 :(得分:0)

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);
        }
    }
}