C#のシングルトンとは?「1つだけ」を保証する仕組みをやさしく理解する

広告

ゲームを作っていると、「持ち物リストは、ゲーム全体で1つだけあればいい」という場面が出てきます。アイテムを拾う場所と、持ち物画面を開く場所が別々でも、見ているのは同じ1冊のリストであってほしいからです。

この「1つだけを保証して、どこからでも同じものを使えるようにする」作り方を、シングルトンと呼びます。この記事では、たとえ話から入って、最小のコードで仕組みを理解します。

この記事で分かること

  • シングルトンとは何か、なぜ必要なのか
  • シングルトンの最小コードと、書いてある3つの意味
  • 別のクラスから、同じ1つを使う方法
  • シングルトンの使いすぎに注意したい理由

ポイント1:たとえば「みんなで共有する1冊のメモ帳」

持ち物を書き込むメモ帳が、2冊あったとします。敵を倒した場所では、1冊目のメモ帳に「木」と書きました。ところが、持ち物画面を開くと、2冊目のメモ帳を見に行ってしまい、「何もない」と表示されます。

これでは困ります。メモ帳は、ゲーム全体で1冊だけにして、誰が見に行っても同じ1冊であってほしいのです。そのために、「1冊しか作れない」ようにしておく作り方が、シングルトンです。

ポイント2:シングルトンとは

シングルトン(Singleton)は、「そのクラスのインスタンスを、1つだけにする」作り方です。次の2つを満たします。

  • インスタンスが、ゲーム全体で1つしか作られない
  • どこからでも、同じ1つにアクセスできる

インスタンスが何か、まだ曖昧なときは、C#のクラスとインスタンスをメモリ割り当ての図で理解するを先に読むと、理解しやすくなります。

ポイント3:最小のコードで見てみる

持ち物のメモ帳を、シングルトンで作ると、次のようになります。

public class OwnedItemsData
{
    // ① ゲーム全体で1つだけの本体。ここで1回だけ作る
    public static OwnedItemsData Instance { get; } = new OwnedItemsData();

    // ② private にして、外から new できなくする
    private OwnedItemsData() { }

    // ③ 持ち物のデータと、それを使うメソッド
    private readonly List<string> items = new List<string>();

    public void Add(string name)
    {
        items.Add(name);
    }

    public int Count
    {
        get { return items.Count; }
    }
}

書いてあることは、3つだけです。

  1. ① public static ... Instance { get; } = new ...:クラスの中に、自分自身のインスタンスを1つ持たせます。static なので、new しなくても、OwnedItemsData.Instance で呼び出せます。static の考え方は、なぜstaticをつけるとnewが要らなくなるのか?税込み価格計算で理解するstatic入門で説明しています。
  2. ② private OwnedItemsData() { }:コンストラクタを private にします。これで、外から new できなくなり、「1つだけ」が守られます。
  3. ③ 持ち物のデータと、それを使うメソッド:ふつうのクラスと同じように書きます。

外から new しようとすると、次のようにエラーになります。

// 外から new しようとすると、エラーになる
var other = new OwnedItemsData(); // コンパイルエラー

ポイント4:別のクラスから、同じ1つを使う

作ったメモ帳は、どのクラスからでも、OwnedItemsData.Instance で呼び出せます。

// アイテムを拾ったとき(拾う処理を書いたクラスから)
OwnedItemsData.Instance.Add("木");

// 持ち物画面を開いたとき(画面を作るクラスから)
Debug.Log(OwnedItemsData.Instance.Count); // 1 と表示される

拾う処理と、画面を作る処理は、別のクラスです。それでも、同じ1冊のメモ帳を見ているので、拾った「木」が、持ち物画面にも出てきます。

ポイント5:使いすぎに注意

シングルトンは便利ですが、何でもシングルトンにすると、困ることがあります。

  • どこからでも書き換えられるので、データが変わった原因を探しにくくなる
  • クラス同士が、Instance に頼り切りになり、あとで直しにくくなる
  • テストのときに、毎回、状態をリセットしないといけなくなる

目安は、「ゲーム全体で、本当に1つだけでよいか」です。持ち物のリスト、ゲームの設定、セーブデータのように、全体で共有するものに、絞って使うと安心です。

ポイント6:もう一歩先へ

ここで使った { get; } = new ... の1行の書き方には、「使う直前まで作らない」if 文の書き方もあります。2つの違いと、使い分けは、C#のシングルトン:{ get; } = new … の1行と if 文(遅延初期化)はどう違う?で詳しく説明しています。

実際のゲームで、持ち物リストにどう使うかは、【Unity入門】アイテムを拾って持ち物に表示する最小コードと、【Unity入門】アイテム所持の仕組みを作ろう ― 「データ」と「見た目」を分ける考え方で紹介しています。

まとめ

  • シングルトンは、インスタンスを1つだけにして、どこからでも同じものを使えるようにする作り方
  • private のコンストラクタと、static な Instance の2つが、基本の形
  • 全体で本当に1つだけでよいものに、絞って使う

まずは、持ち物のメモ帳のような「全体で1つ」のデータで、試してみてください。

訪問数 6 回, 今日の訪問数 9回

広告

C#

Posted by hidepon