0

我想找到一个简洁的单例模式 C# 实现,我想知道它是否应该支持以下场景。如果我的单例的私有构造函数,作为初始化实例的一部分,调用一个本身试图访问当前正在初始化的单例的构造函数怎么办?这就是我在这个问题的标题中所说的可重入的意思。有人会说构造函数不应该做任何可能导致这种情况发生的足够复杂的事情。但是,如果由于调用的构造函数中的某些未来代码更改,它确实发生了怎么办?

我浏览了这个问题的各种答案。使用的简洁性给我留下了深刻的印象Lazy<T>。在我正在查看的用例中,它会引发一个异常,这比构造两个单例实例要好得多但是很可惜,在抛出异常时,它会使应用程序崩溃,这意味着它不支持目标场景。

class Apple
{
    static readonly Lazy<Apple> instance = new Lazy<Apple>( () => new Apple(), true );
    public static Apple Instance { get { return instance.Value; } }

    private Apple()
    {
        // Call other methods which might access Instance...
        var testGet = Instance;
    }
}

所以我有想法改为执行以下操作。

class Banana
{
    static Banana instance;
    public static Banana Instance { get { return instance ?? new Banana(); } }

    private Banana()
    {
        instance = this;
        // Then call other methods which might access Instance...
        var testGet = Instance;
    }
}

它支持目标场景,但它有什么问题吗?有更好的解决方案吗?

在发布答案之前,请注意以下事项。和很多人一样,我仍然认为 Singleton 是一种模式。许多 DI 爱好者喜欢称其为反模式。在依赖 DI/IoC 的项目的上下文中,这是一个合理的断言。然而,在这种情况之外,Singleton 仍然是一种有效的设计模式。我不使用 DI,也没有兴趣在这里讨论它的优点。如果它将 Singleton 称为“反模式”,或者它将提供“依赖注入”作为解决方案,请不要在下面发布答案。提前致谢。

4

1 回答 1

1

尽管 Singleton 可能不被视为反模式,但在实例完全构造之前访问实例的成员一种反模式。您不能保证正在初始化的类外部的代码不会尝试使用一些未初始化的状态。所以应该支持有问题的场景。如果稍后将尝试访问单例的代码添加到由单例的构造函数调用的构造函数中,那么这应该会触发重新设计。

使用Lazy<T>as Appledemo 是更好的方法,因为它会在重新进入时引发异常,而不是访问不完整的实例。 Lazy<T>如果需要,还支持从多个线程进行访问。

于 2013-09-17T19:40:37.197 回答