我想找到一个简洁的Singleton模式的C#实现,我想知道它是否应该支持以下场景。如果我的单例的私有构造函数,作为初始化实例的一部分,调用一个自己试图访问当前正在初始化的单例的构造函数,该怎么办?这就是reentrancy在这个问题标题中的含义。有些人会说构造函数不应该做任何足够复杂的事情来导致这种情况发生。但是,如果由于调用的构造函数中的某些未来代码更改,确实会发生这种情况呢?
我查看了this question的各种答案。使用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称为“反模式”,或者它将提供“依赖注入”作为解决方案,请不要在下面发布答案。提前谢谢。
答案 0 :(得分:1)
虽然Singleton可能不被视为反模式,但在该实例完全构造之前访问实例的成员是反模式。您不能保证正在初始化的类外部的代码不会尝试使用某些未初始化的状态。因此,相关方案应该不支持。如果稍后将尝试访问单例的代码添加到由单例的构造函数调用的构造函数中,则应触发重新设计。
使用Lazy<T>
作为Apple
演示是更好的方法,因为它会在重新进入时抛出异常,而不是访问不完整的实例。如果需要,Lazy<T>
还支持来自多个线程的访问。