这是在C#中实现此模式的最佳方法吗?

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();
    public static Singleton Instance { get { return instance; } }

    static Singleton() {}
    private Singleton() {}
}


评论

Singleton是反模式。

请注意:“全局状态和单例”

#1 楼

我在C#中使用Jon Skeet的完全安全的实例化线程安全Singleton版本:真的很容易使用,只需像这样从Instance属性获取实例; SingletonName instance = SingletonName.Instance;

评论


\ $ \ begingroup \ $
我认为Instance就足够了,但是我认为是YMMV。
\ $ \ endgroup \ $
–所罗门
2011年1月20日在12:08

\ $ \ begingroup \ $
嵌套类使它变得懒惰。在调用Singleton.Instance之前,运行时不会实例化Singleton实例。这样,如果您在Singleton上还有其他静态成员或其他成员,则可以在不创建Singleton实例的情况下访问它们。
\ $ \ endgroup \ $
– Wes P
2011年1月20日15:55

\ $ \ begingroup \ $
我想鼓励大家阅读Jon Skeet的文章(在Zolomon提供的链接之后),因为它涵盖了该主题的几乎所有答案,并列出了他们的利弊。
\ $ \ endgroup \ $
–ShdNx
2011年1月21日,11:37

\ $ \ begingroup \ $
这也是线程安全的。
\ $ \ endgroup \ $
– David Basarab
2011年1月27日17:43

\ $ \ begingroup \ $
@Chris:你不会的。这就是为什么单例很烂的原因-它们本质上是隐藏的全局状态,几乎不可能可靠地测试包含或使用它们的代码。
\ $ \ endgroup \ $
– cHao
2011-10-28 11:54



#2 楼

我一直使用它,它允许泛型的延迟初始化,而不是为我想要的每种单身人士类型创建一个新的单身人士类。它也是线程安全的,但不会在每次访问时都使用锁。比锁。三种可能的情况:

1)第一次通过单个线程访问。可能与使用锁的明显方法的性能大致相同。

2)多个线程同时进行首次访问。许多线程可能会进入互锁的交换,在这种情况下,可能会构造多个项,但只有1个会“获胜”。只要您的构造函数没有全局副作用(这是不应该的),行为就将是正确的。由于有多个分配,因此性能将略低于锁,但是开销很小,这是非常罕见的情况。

3)以后的访问。没有锁定或互锁操作,这几乎是最佳选择,显然是大多数情况。

评论


\ $ \ begingroup \ $
+1是一个很好的解决方案,但是要求构造函数没有副作用的问题2总是使我不敢采用这种方法。
\ $ \ endgroup \ $
–GWLlosa
2011年1月21日,下午3:59

\ $ \ begingroup \ $
您的构造函数多久出现一次副作用?我一直认为这是一种很不好的代码味道。特别是在多线程代码中!
\ $ \ endgroup \ $
–马丁
2011年1月21日13:41

\ $ \ begingroup \ $
另外,您的构造函数可以具有副作用,只要它们是线程安全的,并且不要假定这是唯一要创建的实例。请记住,可能会创建多个实例,但只有一个实例会“获胜”,因此,如果确实有副作用,则最好正确实施!
\ $ \ endgroup \ $
–马丁
2011年1月21日15:04

\ $ \ begingroup \ $
@Martin:难道不是单例的全部目的是只能创建一个实例吗?在我看来,依靠这一假设是一件相当合理的事情……
\ $ \ endgroup \ $
– cHao
2011-10-28 11:58



\ $ \ begingroup \ $
-1,不是线程安全的(构造函数可能被多次调用),因此无法达到单例的目的。
\ $ \ endgroup \ $
–康拉德·鲁道夫(Konrad Rudolph)
2012年11月19日在7:53

#3 楼

如果使用的是.NET 4.0,则可以利用System.Lazy类。这样可以确保实例仅创建一次。

评论


\ $ \ begingroup \ $
您应该添加一个代码示例,因为我认为这是迄今为止用.NET 4.0实现单例的最佳方法。
\ $ \ endgroup \ $
– Scott Lerch
2012年6月22日在1:05

\ $ \ begingroup \ $
csharpindepth.com/Articles/General/Singleton.aspx(“第六版”)中提供了代码示例。
\ $ \ endgroup \ $
– Konrad Morawski
2013年12月9日在9:30

#4 楼

我使用的模式类似于已经发布的模式,但有以下区别:

public sealed class Logging
{
    static Logging instance = null;
    static readonly object lockObj = new object();

    private Logging()
    {
    }

    public static Logging Logger
    {
        get
        {
            **if (instance == null)**
            {
                 lock (lockObj)
                 {
                     if (instance == null)
                     {
                         instance = new Logging();
                     }

                 }
            }
            return instance;
        }
    }


}

原因是它避免调用锁每次都能为您提供帮助。因此,您检查是否为空,然后将其锁定(如果为空)。然后,您必须再次检查是否为空(因为有人可能第二秒就进入了),但是您应该没问题。这样,您只会在第一次(或两次)打到锁,然后吹过它而不会在其余时间锁定。

评论


\ $ \ begingroup \ $
很好的例子。我只需要添加名称:它被称为“双重检查锁定”
\ $ \ endgroup \ $
–奥列格·赖巴克(Oleg Rybak)
2011年1月21日在10:33

\ $ \ begingroup \ $
return语句必须在第一个if(instance == null)语句之外。否则,如果实例已经存在,它将不会返回实例。
\ $ \ endgroup \ $
–丹
2011年1月21日13:30

\ $ \ begingroup \ $
@Dan很正确,我的错。
\ $ \ endgroup \ $
–GWLlosa
2011年1月21日,13:56

#5 楼

如果必须在C#中实现Singleton,则始终使用此解决方案。与您的解决方案相比,此解决方案是线程安全的,并且使用了惰性创建方法。 (仅在实际需要时才实例化该对象)。

在这个示例中,急切与懒惰的创建并不值得讨论,但是我将尽可能使用线程安全的实现。

评论


\ $ \ begingroup \ $
这是一个非常糟糕的解决方案,它在每次访问时都使用一个锁。这具有非常高的开销。
\ $ \ endgroup \ $
–马丁
2011年1月21日,0:38

\ $ \ begingroup \ $
@Martin:除非您每次都说Logging.Logger.DoThis()和Logging.Logger.DoThat(),否则开销不会那么麻烦。无论如何,我还是希望避免这种情况,因为(1)它几乎根深了Logging类,使以后换用其他东西更加痛苦,并且(2)Logging。一个单例,总是返回同一件事,因此每次重新获取都是很愚蠢的。哦,还有(3)这是重复的,我讨厌重复。
\ $ \ endgroup \ $
– cHao
2011-10-28 12:03



\ $ \ begingroup \ $
您现在正在使用单例,但是稍后您可能希望更改单例,例如,当文件已满时,它将换成新的记录器-因此,我倾向于避免缓存单例。当您可以轻松高效地将访问集中在一个地方时,例如,没有必要保留数百个对某事的引用。双重检查锁定
\ $ \ endgroup \ $
–马丁
2011-10-28 13:41



#6 楼

像Unity这样的依赖注入容器支持单例概念。 Unity来自Microsoft(并且是开源的),但是有很多开源的DI容器。

#7 楼

这是来自Wikipedia的双重检查锁定页面的示例:有关说明,请参见“双重检查的锁定已损坏”声明。 (它是为Java编写的,但适用相同的原理。)

#8 楼

这是使用Lazy<T>的实现:

public class Foo : IFoo
{
    private static readonly Lazy<IFoo> __instance
        = new Lazy<IFoo>(valueFactory: () => new Foo(), isThreadSafe: true);

    private Foo() { }

    public static IFoo Instance { get { return __instance.Value; } }
}


#9 楼

我想提出一种使用volatile关键字的简单方法,静态变量不是线程安全的,但是原子执行被认为是线程安全的。通过使用volatile,我们可以强制执行原子操作。

public sealed class Singleton
{
    private static **volatile** readonly Singleton instance = new Singleton();
    public static Singleton Instance { get { return instance; } }
    private Singleton() { }
}


锁定不是确定的方法,因为它很昂贵。

我喜欢约翰·斯凯特(John skeet)的创建对象的单例创建方法。