(:3[kanのメモ帳]

個人ゲーム開発者kan.kikuchiのメモ的技術ブログ。月木更新でUnity関連がメイン。

Serializableなクラスをフィールドにしたら、nullなのにコンストラクタが実行される件【C#】【Unity】【トラブルシューティング】


このエントリーをはてなブックマークに追加

この記事でのバージョン
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なので、コンストラクタが実行される事が無い様に見えますが、

実はコンストラクタは実行されています。


f:id:kan_kikuchi:20170726073348j:plain


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'.

f:id:kan_kikuchi:20170726075548j:plain


さらに、コンパイルが完了した時(右下のくるくるが無くなった時)にも

コンストラクタが実行されてしまうという問題もあります。


f:id:kan_kikuchi:20170726080433g:plain


解決策

この問題はフィールドにNonSerializedという属性を付加する事で解決できます。

NonSerializedにする事でシリアライズされなくなり、コンストラクタも実行されません。

//NonSerializedを付けた事によりシリアライズされなくなる。
[NonSerialized]
private SerializableClass _serializableClass = null;


もちろん_serializableClassがシリアライズされないと困る!

という場合には使えない解決策なのでご注意を。