C# 单例的延迟初始化

C# 单例的延迟初始化,c#,.net,design-patterns,singleton,C#,.net,Design Patterns,Singleton,在阅读时,我开始想为什么我们首先需要惰性初始化。本条的第四种方法似乎已经足够了,此处仅供参考: public sealed class Singleton { static readonly Singleton instance=new Singleton(); // Explicit static constructor to tell C# compiler // not to mark type as beforefieldinit static Sing

在阅读时,我开始想为什么我们首先需要惰性初始化。本条的第四种方法似乎已经足够了,此处仅供参考:

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;
        }
    }
}
在极少数情况下,在单例上有其他静态方法,延迟初始化可能有好处,但这不是一个好的设计


因此,人们能告诉我为什么惰性初始化如此热门吗?

在这些场景中,无论您正在初始化什么,都可能根本不需要,并且初始化成本很高(以CPU周期或资源为单位),那么在不需要对象的情况下,实现惰性初始化器可以节省成本

如果对象总是需要的,或者初始化成本相对较低,那么惰性初始值设定项没有额外的好处


在任何情况下,不正确地实现惰性初始值设定项都会使单例非线程安全,因此,如果需要此模式,请小心正确地实现它。乔恩的文章有一个模式(我认为这是最后一个),可以解决这个问题

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

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

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

单例解决了这一问题,因为一旦调用GetInstance(),无论您在初始化过程中处于何处,对象都保证存在


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

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

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


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

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

惰性初始化实现的选择很棘手,在spring中,您会选择lazy init=“true”(spring急切地实例化Singleton)、init方法/destroy方法、@PostConstruct、initializeBean-afterPropertiesSet()方法还是在getInstance()方法中返回相同的spring实例


选择是在spring容器外部的可测试性和可重用性之间进行权衡。

还要注意,在.NET4中,首选的方法可能是使用
System.Lazy
()我的问题是,我没有看到惰性初始化的用例——如果我们决定使用它,我们总是需要初始化一个单例类,但是,如果我们不调用使用单例对象的方法,它就不会被创建,所以我没有看到用例。@Lenik,听起来你不需要(也不应该)使用惰性初始化器-没问题。@Charles,我看不出有人需要它。即使我不使用惰性初始化,我仍然可以有效地得到它,因为静态属性不会初始化,除非我实际使用它。@Lenik,如果有人引用你的类来使用其他不需要什么的属性或类成员怎么办是否有对象是按需惰性地初始化的?即使是在单例中也是如此(这不是一个好的设计)但是如果singleton类有一个静态实用程序方法呢?它可能会在根本不需要singleton实例的情况下被调用…如果你在静态初始化器中初始化singleton实例,它将被不必要地初始化。好吧,所以我不疯狂。似乎每个人都被某种形式的惰性初始化所困扰实际情况是用例几乎不存在。惰性初始化作为一般实践非常有用,但不一定适用于单例。他们为什么称之为“惰性初始化”呢?似乎这样做根本不是惰性的,称之为“复杂初始化”或“有问题的初始化”,或者“容易出现缺陷的初始化”…其中一个可以更准确地描述它;)延迟初始化可以保证线程安全,而不需要特殊的线程安全构造。[1]Jon Skeet的文章已经转移到。另外,棘手的是,您将在哪里初始化资源,在构造函数中?或者在每次返回相同实例的容器调用的另一个方法中?同样,棘手的是,您将在哪里初始化资源,在构造函数中?或者在ret的容器调用的另一个方法中每次URN都是同一个实例?在将实例化委托给spring(调用initialize())的情况下,单元测试需要显式调用initialize(),以获取有效实例。例如,spring允许使用私有构造函数创建单例,但允许在调用getInstance()时进行初始化这会将初始化推送到另一个方法而不是构造函数中,但是您将如何测试/模拟?