サーチ…


静的に初期化されたシングルトン

public class Singleton
{
    private readonly static Singleton instance = new Singleton();
    private Singleton() { }
    public static Singleton Instance => instance;
}

この実装はスレッドセーフです。この場合、 instanceオブジェクトは静的コンストラクタで初期化されるためです。 CLRは、すべての静的コンストラクターがスレッドセーフで実行されるようにします。

突然変異のinstanceはスレッドセーフな操作ではないため、 readonly属性は初期化後の不変性を保証します。

レイジーでスレッドセーフなシングルトン(Double Checked Lockingを使用)

このシングルスレッドのスレッドセーフバージョンは、 static初期化がスレッドセーフであることが保証されていない初期バージョンの.NETでは必要でした。より現代的なバージョンのフレームワークでは、以下のパターンで実装間違いを起こしやすいので、通常は静的に初期化されたシングルトンが優先されます。

public sealed class ThreadSafeSingleton
{
   private static volatile ThreadSafeSingleton instance;
   private static object lockObject = new Object();

   private ThreadSafeSingleton()
   {
   }

   public static ThreadSafeSingleton Instance
   {
      get 
      {
         if (instance == null) 
         {
            lock (lockObject) 
            {
               if (instance == null)
               {
                  instance = new ThreadSafeSingleton();
               }
            }
         }

         return instance;
      }
   }
}

if (instance == null)チェックは、ロックを取得する前に1回、その後に1回、2回行われます。この実装は、最初のヌルチェックを行わなくてもスレッドセーフになります。しかし、これは、インスタンスが要求されるたびにロックが取得さ 、パフォーマンスが低下する原因になることを意味します。最初のヌルチェックは、必要でない限りロックが取得されないように追加されます。 2回目のヌルチェックでは、ロックを取得する最初のスレッドだけがインスタンスを作成することを確認します。他のスレッドは、インスタンスを作成して先にスキップすることができます。

遅延スレッドセーフなシングルトン(Lazyを使用)

.Net 4.0型レイジーはスレッドセーフなオブジェクトの初期化を保証しているので、この型を使用してシングルトンを作ることができます。

public class LazySingleton
{
    private static readonly Lazy<LazySingleton> _instance =
        new Lazy<LazySingleton>(() => new LazySingleton());
 
    public static LazySingleton Instance
    {
        get { return _instance.Value; }
    }

    private LazySingleton() { }
}

Lazy<T>を使用すると、オブジェクトが呼び出しコードのどこかで使用されている場合にのみインスタンス化されます。

簡単な使い方は次のようになります:

using System;
                    
public class Program
{
    public static void Main()
    {
        var instance = LazySingleton.Instance;
    }
}

.NET Fiddleのライブデモ

遅延のある、スレッドセーフなシングルトン(.NET 3.5以降、代替実装)

.NET 3.5以降では、 Lazy<T>クラスを持たないため、次のパターンを使用します。

public class Singleton
{
    private Singleton() // prevents public instantiation
    {
    }

    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }
    
    private class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }

        internal static readonly Singleton instance = new Singleton();
    }
}

これはJon Skeetのブログ記事からインスピレーションを得ています。

Nestedクラスはネストされ、プライベートなので、 Sigletonクラスの他のメンバーにアクセスすることによって(例えば、public readonlyプロパティなど)、シングルトンインスタンスのインスタンス化はトリガーされません。

Singletonインスタンスが不要になったときに破棄する

ほとんどの例では、アプリケーションがもはやそのオブジェクトを必要としなくても、所有アプリケーションが終了するまでLazySingletonオブジェクトをインスタンス化して保持しています。これに対する解決策は、 IDisposableを実装し、次のようにオブジェクトインスタンスをnullに設定することです。

public class LazySingleton : IDisposable
{
    private static volatile Lazy<LazySingleton> _instance;
    private static volatile int _instanceCount = 0;
    private bool _alreadyDisposed = false;

public static LazySingleton Instance
{
    get
    {
        if (_instance == null)
            _instance = new Lazy<LazySingleton>(() => new LazySingleton());
        _instanceCount++;
        return _instance.Value;
    }
}

private LazySingleton() { }

// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{ 
    if (--_instanceCount == 0) // No more references to this object.
    {       
       Dispose(true);
       GC.SuppressFinalize(this);           
    }
}

// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
    if (_alreadyDisposed) return; 
  
    if (disposing) 
    {
        _instance = null; // Allow GC to dispose of this instance.
        // Free any other managed objects here.
    }
  
    // Free any unmanaged objects here.
    _alreadyDisposed = true;
}

上記のコードは、アプリケーションが終了する前のインスタンスを破棄しますが、コンシューマが毎回使用後にオブジェクトに対してDispose()を呼び出す場合に限ります。これが起こるという保証やそれを強制する方法はないので、インスタンスが処分されるという保証もありません。しかし、このクラスが内部で使用されている場合は、 Dispose()メソッドが使用するたびに呼び出されることを保証する方が簡単です。例を次に示します。

public class Program
{
    public static void Main()
    {
        using (var instance = LazySingleton.Instance)
        {
            // Do work with instance
        }
    }
}

この例はスレッドセーフではありません



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow