12
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

JetBrains製で「赤ペン先生」とも呼ばれるVisual Studioプラグイン「Reshaper」、そしてそれを搭載した.NET向けIDE「Rider」

素晴らしい機能を多く持っている、Reshaper/Rider。愛用しているUnity開発者も、多いのではないでしょうか?

そんなReshaper/Riderの機能の一つ「Code Inspections」。コードの良くない部分を指摘してくれたり、「こうすればよくなるよ」という修正をしてくれます。

さて、Unity固有の「Code Inspections」には、ちょっと背景知識が必要でわかりにくいものがあります。それが、

「Possible unintended bypass of lifetime check of underlying Unity engine object」

です。

Riderでは、こんな感じで出ます。

キャプチャ.PNG

この投稿ではその背景・内容を紹介します。

背景知識1

GameObjectやComponentをnullと比較するとき、落し穴があります。

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

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

上のコードで、変数targetがオブジェクトを参照していてnullじゃないのに、target == nullがtrueになります。

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

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

関連記事 : 「GameObjectやComponentをnullと比較するときの落し穴

背景知識2

C#で「person?.Name」と「person == null ? null : person.Name」は等価じゃありません。

null条件演算子 (?.) において、nullとの比較に 「==」オペレーターでの比較は行われなません。(型に対して==で独自定義した処理は呼ばれない)

同様に、null合体演算子 (??)でも同じように、nullとの比較に 「==」オペレーターでの比較は行われない。(型に対して==で独自定義した処理は呼ばれない)

関連記事 : 「C#で「person?.Name」と「person == null ? null : person.Name」は等価じゃない。

背景知識1と背景知識2を合わせての注意点

次のコード1と

// コード1
[SerializeField] private GameObject target;

private void Awake()
{
    DestroyImmediate(target);
    Debug.Log(target == null ? null : target.name); // `Null`と表示される
}

次のコード2は、

// コード2
[SerializeField] private GameObject target;

private void Awake()
{
    DestroyImmediate(target);
    Debug.Log(target?.name); // `UnassignedReferenceException`が投げられる
}

は結果が異なります。

コード1では==オペレーターを使っているので、targetが生存していなため(DestroyImmediateで破壊している)、target == nullはtrueとなり、ログにNullと表示されます。

コード2ではnull条件演算子(?.)を使っているので、==が呼ばれません。コード2ではオブジェクトの生存に関係なく処理が進みます。その結果、処理が進みオブジェクトが破壊されているためUnassignedReferenceExceptionが投げられます。

ReShaper/RiderのCode Inspectionの意味は?

「背景知識1と背景知識2を合わせての注意点」

でReShaper/RiderのCode Inspectionの

「Possible unintended bypass of lifetime check of underlying Unity engine object」

意味と理由とメリットが伝わったでしょうか?

GameObjectやComponentをnullとの比較で==を使っている場合、ライフサイクルを加味した処理を行われます。一方で、null条件演算子(?.)やnull合体演算子(??)ではライフサイクルが加味されません。一見、同じように見えるコードでも違うことに十分に注意しましょう。

詳しくはこちらも!

JetBrains/resharper-unityのwiki「Possible unintended bypass of lifetime check of underlying Unity engine object

個人的な意見

個人的には、UnityEngine.Objectの生存状態で==!=の結果が変わるの、分かりにくい仕様だと思うんですよね。。。

12
10
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
12
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?