C#でスレッドセーフなシングルトンクラスを実装したい


タイトルの件、WCFサービス上でシングルトンクラスを扱うことを想定してスレッドセーフな  
実装をしたいと考えております。  
検索もしてみたのですが、いくつか方法があるようで実際どれが正しいのか  
よく分かりません。。

大変恐縮ですが、サンプルコード等のリンクでも構いませんので、  
ご教示頂けないでしょうか？




シングルトンパターンにはおおむね3パターンの実装方法があります。

最初の方法は

[code]

    class Hoge
    {
        private static Fuga _Piyo;
        public static Fuga Piyo => _Piyo ?? (_Piyo = new Fuga());
    }
    
[/code]

とプロパティにアクセスしたタイミングでインスタンスを作成するものです。この方法は`_Piyo`が`null`かどうかを判定してから`new
Fuga()`を代入するまでの間に他のスレッドでも`new Fuga()`を作成し始め、結果的にインスタンスが複数作成される危険性があります。つまり
**スレッドセーフではありません** 。

この問題はスタティックメンバーの初期化子を使用すると回避できます。

[code]

    class Hoge
    {
        public static Fuga Piyo { get; } = new Piyo();
    }
    
[/code]

このような実装は`static`メンバーの初期化がランタイムにより厳密に一度しか行われないことが保証されるため、`new
Fuga()`が複数回呼び出されることはありません。ですが`Hoge`型が **初期化されるタイミングが不定** という問題があります。

タイミングも制御したい場合は、`static`初期化子でロックオブジェクトを作成します。

[code]

    class Hoge
    {
        private static readonly object _Lock = new object();
        private static Fuga _Piyo;
        public static Fuga Piyo
        {
            get
            {
                lock (Lock)
                {
                    if (_Piyo == null)
                    {
                        _Piyo = new Fuga();
                    }
                    return _Piyo;
                }
            }
        }
    }
    
[/code]

どれが正しいというよりかは、最初の2個の欠点を許容できるかどうかで方法を選べばよいと思います。

なお作成されたインスタンスの実装がスレッドセーフであるかは別問題です。これに関しては個別の設計になりますので

  * ローカルな状態をフィールドで共有しない
  * 状態をインスタンス内で共有する場合は適切にロックを行う

といった程度しかアドバイスできません。




シングルトンパターンにはおおむね3パターンの実装方法があります。

最初の方法は

[code]

    class Hoge
    {
        private static Fuga _Piyo;
        public static Fuga Piyo => _Piyo ?? (_Piyo = new Fuga());
    }
    
[/code]

とプロパティにアクセスしたタイミングでインスタンスを作成するものです。この方法は`_Piyo`が`null`かどうかを判定してから`new
Fuga()`を代入するまでの間に他のスレッドでも`new Fuga()`を作成し始め、結果的にインスタンスが複数作成される危険性があります。つまり
**スレッドセーフではありません** 。

この問題はスタティックメンバーの初期化子を使用すると回避できます。

[code]

    class Hoge
    {
        public static Fuga Piyo { get; } = new Piyo();
    }
    
[/code]

このような実装は`static`メンバーの初期化がランタイムにより厳密に一度しか行われないことが保証されるため、`new
Fuga()`が複数回呼び出されることはありません。ですが`Hoge`型が **初期化されるタイミングが不定** という問題があります。

タイミングも制御したい場合は、`static`初期化子でロックオブジェクトを作成します。

[code]

    class Hoge
    {
        private static readonly object _Lock = new object();
        private static Fuga _Piyo;
        public static Fuga Piyo
        {
            get
            {
                lock (Lock)
                {
                    if (_Piyo == null)
                    {
                        _Piyo = new Fuga();
                    }
                    return _Piyo;
                }
            }
        }
    }
    
[/code]

どれが正しいというよりかは、最初の2個の欠点を許容できるかどうかで方法を選べばよいと思います。

なお作成されたインスタンスの実装がスレッドセーフであるかは別問題です。これに関しては個別の設計になりますので

  * ローカルな状態をフィールドで共有しない
  * 状態をインスタンス内で共有する場合は適切にロックを行う

といった程度しかアドバイスできません。

