【问题标题】:lazy initialization of singletons单例的延迟初始化
【发布时间】:2009-11-12 23:48:03
【问题描述】:

在阅读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;
        }
    }
}

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

那么人们能告诉我为什么延迟初始化如此热门吗?

【问题讨论】:

标签: c# .net design-patterns singleton


【解决方案1】:

在那些你正在初始化的东西可能根本不需要并且初始化成本很高的场景中(就 CPU 周期或资源而言),然后在对象的情况下实现惰性初始化器可以节省成本不需要。

如果对象总是需要,或者初始化相对便宜,那么惰性初始化器不会带来额外的好处。

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

【讨论】:

  • 另请注意,在 .NET 4 中,首选方法可能是使用 System.Lazy<T> (msdn.microsoft.com/en-us/library/dd642331(VS.100).aspx)
  • 我的问题是我没有看到延迟初始化的用例——如果我们决定使用单例类,我们总是需要初始化它,但是,如果我们不调用使用单例对象,它不会被创建,所以我没有看到用例。
  • @Lenik,听起来你不需要(也不应该)使用惰性初始值 - 不是问题。
  • @Charles,我看不出有人需要它。即使我不使用延迟初始化,我仍然可以有效地得到它,因为除非我实际使用它,否则静态属性不会被初始化。
  • @Lenik,如果有人引用您的类以使用其他属性或类成员,而这些属性或类成员不需要任何正在按需初始化的对象,该怎么办?即使在单例中,(这并不是一个好的设计)但是如果单例类具有静态实用程序方法怎么办?在根本不需要单例实例的情况下,它可能会被调用...如果您在静态初始化器中初始化单例实例,它将被不必要地初始化。
【解决方案2】:

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

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

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

【讨论】:

  • 好的,那我没疯。似乎每个人都沉迷于某种形式的惰性初始化,而实际情况是几乎不存在用例。
  • 延迟初始化作为一般实践非常有用,但不一定适用于单例。
【解决方案3】:

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

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

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

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

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

【讨论】:

    【解决方案4】:

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

    惰性初始化实现选择很棘手,在spring中,你会选择lazy-init="true"(spring急切地实例化单例)、init-method/destroy-method、@PostConstruct、InitializingBean - afterPropertiesSet() 方法还是在 getInstance() 方法中返回相同的弹簧实例?

    选择是在可测试性与弹簧容器外的可重用性之间进行权衡。

    【讨论】:

    • 另外,棘手的是你会在哪里初始化你的资源,在构造函数中?还是在每次返回相同实例的容器调用的另一个方法中?
    • 另外,棘手的是你会在哪里初始化你的资源,在构造函数中?还是在每次返回相同实例的容器调用的另一种方法中?在委托实例化的情况下,调用initialize(),给spring,单元测试需要显式调用initialize()来获得一个有效的实例。例如,Spring 允许使用私有构造函数创建单例,但允许在有人在此实例上调用 getInstance() 时进行初始化。这会将初始化推入另一个方法而不是构造函数,但是您将如何测试/模拟?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    • 2020-05-10
    • 1970-01-01
    • 2011-11-17
    相关资源
    最近更新 更多