この記事でのバージョン
Unity 2017.1.0f3
はじめに
今回はタイトル通り、
Serializableなクラスをフィールドにしたら、nullなのにコンストラクタが実行された話です。
コンストラクタが実行される
例えば以下のようにSerializableClassというSerializableなクラスを作成し、
それを別のクラス(NewBehaviourScript)がフィールドとして持ったとします。
using System; using UnityEngine; public class NewBehaviourScript : MonoBehaviour { //Serializableなクラスのフィールド private SerializableClass _serializableClass = null; private void Awake (){ //nullかどうか確認する Debug.Log("_serializableClassはnullかどうか : " + (_serializableClass == null)); } } /// <summary> /// シリアライズされたクラス /// </summary> [Serializable] public class SerializableClass{ /// <summary> /// コンストラクタ(インスタンスが生成された時が分かるようにログを出す) /// </summary> public SerializableClass(){ Debug.Log("SerializableClassが生成されました!"); } }
一見、_serializableClassはずっとnullなので、コンストラクタが実行される事が無い様に見えますが、
実はコンストラクタは実行されています。
Awake時点ではnullなので、その前の段階で一度インスタンスが生成されてしまっているようです。
コンストラクタが実行されると何がマズイ?
予期せぬインスタンスが生成される事自体がそもそもマズイですが、
UnityだとAwake前にインスタンスが生成されているというのが落とし穴です。
例えばコンストラクタ内でResources.Load等を使ってしまうと、
public SerializableClass(){ Debug.Log("SerializableClassが生成されました!"); Resources.Load("Sprite"); }
「シリアライズ中にLoadは使えません、AwakeかStart以降で実行しなさい」とエラーが発生します。
Load is not allowed to be called during serialization, call it from Awake or Start instead. Called from MonoBehaviour 'NewBehaviourScript' on game object 'GameObject'.
さらに、コンパイルが完了した時(右下のくるくるが無くなった時)にも
コンストラクタが実行されてしまうという問題もあります。
解決策
この問題はフィールドにNonSerializedという属性を付加する事で解決できます。
NonSerializedにする事でシリアライズされなくなり、コンストラクタも実行されません。
//NonSerializedを付けた事によりシリアライズされなくなる。 [NonSerialized] private SerializableClass _serializableClass = null;
もちろん_serializableClassがシリアライズされないと困る!
という場合には使えない解決策なのでご注意を。