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

发布于 2024-11-27 22:38:43 字数 1419 浏览 1 评论 0原文

我需要一个单例:

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

我使用.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();
    }
} 

这打勾几乎所有的框,除了“从外部触发初始化”。由于实际的初始化发生在 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;

}

I need a singleton that:

  • is lazy loaded
  • is thread safe
  • loads some values at construction
  • those values can be queried at any time
  • the initialization MAY happen at some precise time, before the querying begins - so I must be able to trigger it from the outside somehow. Of course, triggering multiple times should only do the initialization once.

I use .NET 3.5.

I've started with Jon Skeet's implementation (5th version) using a static subclass:

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

This ticks almost all the boxes, except the "trigger initialization from outside". Since the actual initialization happens inside the ctor, it can't happen more than once.

How can this be accomplished?

The singleton will be used like this:

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;

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

弄潮 2024-12-04 22:38:43

似乎您可以这样做:

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

Seems like you could do:

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

    }
} 

Or to create a single instance that will be updated:

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

And the third variation based on your immutable comment and removal of Nested class comment:

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();
            }
        }
    }
} 
丿*梦醉红颜 2024-12-04 22:38:43

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

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

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

The first idea I had was to just use a throwaway variable assigned to the singleton's instance, which would (probably?) trigger the initialization

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

... but programming by side-effects is something I try hard to avoid, so let's make the intention a bit clearer.

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

so the usage would be:

static Main() 
{
    //still wondering if the compiler might optimize this call out
    Singleton.Initialize();

    var vals = Singleton.Instance.Values;
}

Btw this would also work:

static Main() 
{
    var vals = Singleton.Instance.Values;
}

Compiler optimization aside, I think this deals with all the requirements.

衣神在巴黎 2024-12-04 22:38:43

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

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

我不确定您是否需要仅单例实现,但如果没有有关 Initialize() 是否每次都运行相同代码或具有某种类型的变量性质的信息,则无法完全回答。

You can set up an Initialize method that can be fired from outside, if you need the initialize to happen later, but if the values are different on each time it is fired, then it cannot be static, which violates the Singleton pattern.

Based on your example, which has no variables, I assume you are just delaying when the initialization happens (routine rather than constructor), but your question suggests you want different values, but if multiple initializations happen close together, it only initializes once, so I am a bit confused on this.

I am not sure you need a Singleton only implmentation, but cannot fully answer without information on whether or not the Initialize() runs the same code every time or has some type of variable nature.

败给现实 2024-12-04 22:38:43

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

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

}

You can use double-checked locking pattern. Just add following code in you Singleton class:

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

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

}
拥有 2024-12-04 22:38:43

你可以做这样的事情

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

Usage

class Program
{
    static void Main(string[] args)
    {
        var i = Singleton.Instance;
        i = Singleton.Instance;

        Console.WriteLine("-----");

        Singleton.Initialize();
        Singleton.Initialize();
        Singleton.Initialize();

        Console.Read();
    }
}

Which 输出

- 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

You can do something like this

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

Usage

class Program
{
    static void Main(string[] args)
    {
        var i = Singleton.Instance;
        i = Singleton.Instance;

        Console.WriteLine("-----");

        Singleton.Initialize();
        Singleton.Initialize();
        Singleton.Initialize();

        Console.Read();
    }
}

Which outputs

- 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
饮惑 2024-12-04 22:38:43
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);
        }
    }
}
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);
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文