1つだけのインスタンス

2023年10月14日

クラスからインスタンスを作成する場合、別々のメモリーエリアに情報が確保され、別扱いとなります
同じようにインスタンスを作った場合でも、同じエリアにアクセスできるようにする方法を考えてみましょう

シングルトンパターン

シングルトンと呼ばれるデザインパターン(定石、ノウハウ)をみていきましょう

サンプルコード

サンプルコードは、VisualStudio2022、.Net6のコンソールアプリで作成されています

Singleton singleton1 = new Singleton(); 

class Singleton
{
    static Singleton instance;

    Singleton()
    {
    }

    public static Singleton GetInstance()
    {
        // 初期化
        if (instance == null)
        {
            instance = new Singleton();
        }

        return instance;
    }
}

インスタンスを作る部分

new演算子で新しくインスタンスを作ろうとしています

Singleton singleton1 = new Singleton(); 

しかし、これはエラーになります

エラー CS0122 'Singleton.Singleton()’ はアクセスできない保護レベルになっています

コンストラクタのアクセス修飾子がprivateになっているからですね(省略するとprivate)
publicだと作成できます

Singleton()
{
}

では、どのようにしてインスタンスを作るのでしょう

instanceフィールドがSingleton型で宣言されています(static)

GetInstanceメソッドは、戻り値の型がSingletonになります
このメソッドが呼ばれるとどのように処理されるでしょう

最初は、instanceがnullなので、new Singleton()で自身のインスタンスを作成して戻されます
2回目の呼び出しでは、nullではないので最初に作成したインスタンスを戻します

// 宣言
static Singleton instance;

// メソッド
public static Singleton GetInstance()
{
    // 初期化
    if (instance == null)
    {
        instance = new Singleton();
    }

    return instance;
}

呼び出すには次のコードでいいです

Singleton singleton1 = Singleton.GetInstance();

Singletonクラスに次のメンバーを追記してみましょう

public int hp;

呼び出すコードを次のようにして確認します

Singleton singleton1 = Singleton.GetInstance();
singleton1.hp = 10;
Console.WriteLine(singleton1.hp);

結果

10

では、次のコードはどうでしょう

Singleton singleton1 = Singleton.GetInstance();
singleton1.hp = 10;
Console.WriteLine(singleton1.hp);

Singleton singleton2 = Singleton.GetInstance();
singleton2.hp = 20;
Console.WriteLine(singleton1.hp);

結果

10
20

同じインスタンスにアクセスしているのがわかります

全てのコード

Singleton singleton1 = Singleton.GetInstance();
singleton1.hp = 10;
Console.WriteLine(singleton1.hp);

Singleton singleton2 = Singleton.GetInstance();
singleton2.hp = 20;
Console.WriteLine(singleton1.hp);

class Singleton
{
    static Singleton instance;

    public int hp;

    Singleton()
    {
    }

    public static Singleton GetInstance()
    {
        // 初期化
        if (instance == null)
        {
            instance = new Singleton();
        }

        return instance;
    }
}

全てのコード(C#プロパティ版)

上記は、Javaでもほぼそのままで動作します
次のコードはC#特有のプロパティを使ったコードになります
シンプルになりましたね

Singleton singleton1 = Singleton.Instance;
singleton1.Hp = 10;
Console.WriteLine(singleton1.Hp);

Singleton singleton2 = Singleton.Instance;
singleton2.Hp = 20;
Console.WriteLine(singleton1.Hp);

class Singleton
{
    Singleton() { }

    public static Singleton Instance { get; } = new Singleton();

    public int Hp { get; set; }
}

クラス図

参考資料

シングルトンパターンの定義

シングルトンパターン(Singleton Pattern)は、ソフトウェアデザインパターンの一つで、特定のクラスがインスタンス化される際に常に同じ唯一のインスタンスを返すように設計するためのパターンです。シングルトンパターンは、以下の特徴を持ちます:

  1. 唯一のインスタンス: シングルトンクラスは、アプリケーション内で唯一のインスタンスを持ちます。これにより、複数のインスタンスが誤って生成されることを防ぎ、一貫性を保つことができます。
  2. グローバルなアクセスポイント: シングルトンクラスの唯一のインスタンスは、アプリケーション内のどこからでもアクセス可能です。これは、共有リソースや設定情報などの共有データを管理するために便利です。
  3. 遅延初期化: シングルトンインスタンスは必要になるまで作成されないように設計されることがあります。これにより、リソースの効率的な利用や初期化の遅延が可能となります。

シングルトンパターンは、多くのプログラムで使用され、特にリソースの共有や設定管理のために役立ちます。一般的な実装方法には、次のようなものがあります:

  1. 静的メンバー変数: クラス内にプライベートな静的メンバー変数を持ち、唯一のインスタンスを格納します。コンストラクタをプライベートにし、公開された静的メソッドを通じて唯一のインスタンスにアクセスします。
  2. 初期化ブロック: 静的な初期化ブロックを使用して、唯一のインスタンスを初期化します。
  3. Enumを使用: 列挙型を使用してシングルトンを実装する方法もあります。Javaなどのプログラミング言語では、列挙型がシングルトンを実現するためのシンプルで安全な方法として利用されます。

シングルトンパターンは、複数のスレッドが同時にアクセスする場合の競合条件に対処する必要がある場合、注意が必要です。適切な同期メカニズムを使用して、スレッドセーフなシングルトン実装を確保することが重要です。

Unityで使う場合

サウンドが途切れないシーンの切り替えノウハウも紹介されています