懒惰的单例初始化

时间:2009-11-12 23:48:03

标签: c# .net design-patterns singleton

在阅读Jon Skeet's article on singletons in C#时,我开始想知道为什么我们首先需要延迟初始化。似乎文章中的第四种方法应该足够了,这里仅供参考:

public sealed class Singleton
{
    static readonly Singleton instance=new Singleton();

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Singleton()
    {
    }

    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }
}

在极少数情况下,如果在单例上有其他静态方法,延迟初始化可能会有好处,但这不是一个好的设计。

那么人们可以告诉我为什么懒惰的初始化是如此热门的事情?

4 个答案:

答案 0 :(得分:10)

在这些情况下,无论你初始化什么都可能根本不需要,并且初始化起来很昂贵(就CPU周期或资源而言),那么实现一个懒惰的初始化器会在那些对象的情况下节省成本不需要。

如果始终需要该对象,或者初始化相对便宜,那么延迟初始化器没有额外的好处。

无论如何,不​​正确地实现一个惰性初始化程序可以使单例非线程安全,所以如果你需要这种模式,请小心正确。 Jon的文章有一个模式,(我认为这是最后一个),解决了这个问题。

答案 1 :(得分:4)

如果您要使用该类型的唯一原因是引用该实例,则不需要在单例上进行延迟初始化。

但是,如果您引用类型或类型本身的任何其他属性或方法,则将初始化单例。

当然,好的设计会为一种类型留下一个任务,所以这应该不是问题。但是,如果将单例类设置为“复杂”,则延迟初始化可以帮助您避免因过早初始化而导致后果。

答案 2 :(得分:1)

我不确定这也适用于C#,但我会回答C ++:

Singletons背后的一个想法是静态对象的初始化顺序是未定义的。因此,如果您有一个试图使用另一个静态类的静态类,那么您可能遇到了麻烦。

一个具体的例子:假设我有一个Log类,我决定将其作为Singleton实现。我们还假设我有一个名为LoadDB的方法,无论出于何种原因,它都是在程序开始时调用的静态。假设LoadDB决定它需要记录某些内容,它将调用Singleton。但由于静态初始化的顺序未定义,因此它可能正在做一些错误。

Singleton修复此问题,因为一旦调用GetInstance(),无论您在初始化过程中的哪个位置,都保证该对象存在。

同样,我不知道这是否相关,但至少这是它的历史(据我记得)。

答案 3 :(得分:0)

从Java DI的角度来看,延迟初始化是好的,在你可能不想使用的另一个API中总有(例如,spring)bean,例如,一个急切加载的单例缓存(与使用虽然它可能在同一代码中被称为依赖项,但您可能不需要缓存)。加载单身浪费资源是什么意思?

懒惰的初始化实现选择很棘手,在春天,你会选择lazy-init =“true”(spring急切地实例化单例),init-method / destroy-method,@ PostConstruct,InitializingBean - afterPropertiesSet()方法或者在getInstance()方法中返回相同的spring实例?

选择是可测试性与弹簧容器外的可重用性的权衡。