マルチスレッド処理の基礎(6) - フィールドの取り扱い Part 2

前回の記事の続きです。

静的フィールドは宣言時または静的コンストラクタで初期化する

シングルトンのインスタンスのように一度だけ初期化する必要のある静的フィールドは、宣言時または静的コンストラクタで初期化します。サンプルコードの「誤ったコードの例」のように、プロパティアクセス時に排他制御を行わずに初期化済みかどうかの判定を行う場合、競合により以下の問題が発生する可能性があります。

  1. インスタンス生成処理が複数実行される
  2. 初期化途中のインスタンスが外部から参照される
// 正しいコードの例(宣言時に初期化)
public class SafeClass1
{
    private static readonly SafeClass1 def = new SafeClass1();

    public static SafeClass1 Default { get { return def; } }
}

// 正しいコードの例(静的コンストラクタで初期化)
public class SafeClass2
{
    private static readonly SafeClass2 def;

    static SafeClass2()
    {
        def = new SafeClass2();
    }

    public static SafeClass2 Default { get { return def; } }
}

// 誤ったコードの例
public class UnsafeClass
{
    private static UnsafeClass def;

    public static UnsafeClass Default
    {
        get
        {
            if (def == null)
            {
                def = new UnsafeClass();
            }
            return def;
        }
    }
}
補足:Lazyクラス

静的フィールドを初期化する方法には、上記に挙げた方法の他に、.NET Framework 4.0以降に追加されたLazy<T>クラスを使用する方法があります。
フィールド初期化処理の実行タイミングを、型の初期化時ではなくプロパティアクセス時に明示的に遅延させたい場合、Lazyクラスを使用します。

// 正しいコードの例(Lazy<T>を使用)
public class SafeClass3
{
    private static readonly Lazy<SafeClass3> def = new Lazy<SafeClass3>();

    // 例外をキャッシュしたい場合は生成処理のデリゲートを引数に渡す。
    // private static readonly Lazy<SafeClass3> def = new Lazy<SafeClass3>(() => new SafeClass3());

    public static SafeClass3 Default { get { return def.Value; } }
}