MonoBehaviourをSingleton化するための新手法
MonoBehaviourをSingleton化したいことって、まれによくありますよね。これまでボクは、以下の2つの方法を使っていました。
よくやる方法
その1 Singletonパターンを直接記述
public class TestA : MonoBehaviour
{
private static TestA entity;
public static TestA Entity
{
get
{
if (entity == null)
{
entity = UnityEngine.Object.FindObjectOfType<TestA>(true);
if (entity == null)
{
Debug.LogError("ヒエラルキー上に該当GameObjectが存在しない");
}
}
return entity;
}
}
//以下、普通のコードたち
}
いわゆるベタ書きです。基本に忠実ではありますが、毎回これを書くのは面倒ということで、「その2」の方法が編み出されました。
その2 継承を使用
ほかの方々の記事があるのでご参照ください。
「MonoBehaviour Singleton」でググって出てくる解説記事は、ほとんどこの継承を使うタイプのものだと思います。
問題点(?)
さて、この2つの方法でも全然問題ないのですが、それぞれの方法の欠点をあえて挙げるならば、
「その1」の欠点 毎回Singletonを記述するのが面倒
「その2」の欠点 MonoBehaviour継承クラスの派生クラスはSingleton化できない
となるかと思います。2つ目が少し分かりにくいので補足すると、
public class TestB : MonoBehaviour
{
//以下、普通のコードたち
}
//すでにTestBが継承されちゃってるので、
//SingletonMonoBehaviour<T>をさらに継承して使うことはできない(多重継承の禁止)
public class TestBChild : TestB
{
//以下、普通のコードたち
}
こういうことです。C#は多重継承できないので、この方法だと詰みます。
(C#8.0以降のInterfaceのデフォルト実装機能を使えば行けるかもですが、未確認です)
そこで考えました
つまり、手軽に実装できて、MonoBehaviour継承クラス&その派生クラスすべてに適用可能な方法が求められるわけです(ボクだけ?)
そんな怠惰な人たちにお送りします。こちらの方法をご検討ください。
Singleton強制実装クラスちゃん
public class SingletonAttacher<T> where T : MonoBehaviour
{
private T entity;
public T Entity
{
get
{
if (entity == null)
{
entity = UnityEngine.Object.FindObjectOfType<T>(true);
if (entity == null)
{
Debug.LogError("Singleton取得エラー");
}
}
return entity;
}
}
}
今回のキモです。前述のクラスすべてに対してSingletonを差し込める機能を持ったクラスとなります。
実装側
public class TestB : MonoBehaviour
{
//以下、普通のコードたち
}
public class TestBChild : TestB
{
//Singleton実装
public static TestBChild Entity => entity.Entity;
private static SingletonAttacher<TestBChild> entity = new SingletonAttacher<TestBChild>();
//テスト用メソッド
public void TestMethod()
{
Debug.Log("うまくいったね");
}
//以下、普通のコードたち
}
Singleton化したいクラスにstaticフィールドとしてSingletonAttacher<T>を加えてやればOKです。
使用側
public class TestC : MonoBehaviour
{
private void Start()
{
TestBChild.Entity.TestMethod();
}
}
通常のSingletonのときと同じ記述で呼び出せるので、違和感なく使えるかと思います。
おわり
継承ではなく、フィールドを追加するだけであらゆるMonoBehaviour系列クラスをSingleton化できるこの方法、閃いた瞬間、「おいおい自分は神か?」って思ったんですが、いかがでしょうかね??
もっと簡単で完璧な方法をご存知のかたがいらっしゃったら、教えて頂けると嬉しいです。ではではー。
※追記(さらに手軽になりました)
ウェレイさんが、『ジェネリック静的クラス』を用いる改善案を提案してくださいました!ついに実装が1行で済むようになりました!やったー!ありがとうございます!
全体的に諸々調整されていて、完成度はグンと上がっているのではないかと思いますので、ぜひ使ってみてください。
それにしても、これって『ジェネリック静的クラス』のこの上ない使い道ですよね。美しさすら感じるやつです。
Singleton強制実装クラスちゃんver.2
using UnityEngine;
using UnityEngine.Assertions;
public static class SingletonAttacher<T>
where T : Object
{
static T _Entity;
public static T Entity
{
get
{
if (_Entity == null)
{
_Entity = Object.FindObjectOfType<T>(true);
Assert.IsNotNull(_Entity, $"Singleton取得エラーです。{typeof(T).Name}が存在していません。");
}
return _Entity;
}
}
}
実装側
public class TestB : MonoBehaviour
{
//以下、普通のコードたち
}
public class TestBChild : TestB
{
//Singleton実装
public static TestBChild Entity => SingletonAttacher<TestBChild>.Entity;
//テスト用メソッド
public void TestMethod()
{
Debug.Log("うまくいったね");
}
//以下、普通のコードたち
}
使用側
public class TestC : MonoBehaviour
{
private void Start()
{
TestBChild.Entity.TestMethod();
}
}
この記事が気に入ったらサポートをしてみませんか?