LoginSignup
14
9

More than 5 years have passed since last update.

GameObjectやComponentをnullと比較するときの落し穴

Last updated at Posted at 2018-08-08
    [SerializeField] private GameObject target; // 参照している。nullじゃない。

    private void Awake()
    {
        Debug.Log(target == null); // Falseと表示される
        DestroyImmediate(target);
        Debug.Log(target == null); // Trueと表示される
    }

上のコードは、Unityにいくつかある「はまりポイント」や「なんじゃこりゃってなるポイント」の一つです。

変数targetがオブジェクトを参照していてnullじゃないのに、target == nulltrue

解説

GameObjectComponent==オペレーターを使って、target == nullのように評価する際、変数がオブジェクトを参照しているのにも関わらず、trueと評価されることがあります。

これらの親クラスであるUnityEngine.Objectには、==!=boolのオペレーターが定義されています。

そして、その挙動が「対象のGameObjectComponentが、生存していないならば、==nullと比較するとtrueを返す」という仕様になっています

内部でこんなメソッドを作って生存しているかチェックをしています。

  • IsNativeObjectAlive
  • DoesObjectWithInstanceIDExist

この挙動は、私はあまり好きではありません。「コードは、利用者の期待や予測に対して、驚きが最小であるべき」と思っています。この挙動は、==に対して利用者が予期しない「生存チェックもする」という驚きを与えてしまっています。

覚えておかないと、予期せぬバグを発生させることがあるので、注意が必要ですね。(多分、ごくまれに発生するめんどいバグを生み出す原因になりそう)

次のコードを見てみることもオススメします。

おまけ。対応策

変数が参照するオブジェクトが破壊されるケースがあり、純粋にnullと比較したい場合System.Object.ReferenceEqualsを使うことをオススメします。

    [SerializeField] private GameObject target; // 参照している。nullじゃない。

    private void Awake()
    {
        Debug.Log(target == null); // Falseと表示される
        Debug.Log(ReferenceEquals(target, null)); // Falseと表示される
        DestroyImmediate(target);
        Debug.Log(target == null); // Trueと表示される
        Debug.Log(ReferenceEquals(target, null)); // Falseと表示される
    }

参考・関連

こちらもどうぞ

関連記事 : Reshaper/Riderの「Possible unintended bypass of lifetime check of underlying Unity engine object」って何ぞや?

14
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
9