1つだけのインスタンス

2022年10月11日

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

シングルトンパターン

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

サンプルコード

サンプルコードは、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; }
}

クラス図

参考資料

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