コードインスペクション:基盤となる Unity エンジンオブジェクトの有効期間チェックが意図せずバイパスされる可能性があります(「UnityEngine.Object」から派生した型に対するヌル合体を使用すると、基となる Unity エンジンオブジェクトの有効期間チェックがバイパスされます)
UnityEngine.Object から派生した型が、 null 合体演算子 (??) または null 伝播演算子もしくは条件演算子 (?.) のいずれかを使用している場合、この警告が表示されます。 これらの演算子は、 UnityEngine.Object で宣言されているカスタム等価演算子を使用しないため、基となるネイティブ Unity エンジンオブジェクトが破棄されているかどうかのチェックをスキップします。 意図を明確にするためには、明示的な null またはブール値の比較、あるいは System.Object.ReferenceEquals() の呼び出しが推奨されます。
詳細
UnityEngine.Object から派生した型は、C# スクリプトでネイティブの Unity エンジンオブジェクトを表し、操作するために使用されるマネージド .NET オブジェクトです。 これら 2 種類のオブジェクトは、ライフサイクルが異なります。 マネージド .NET オブジェクトは、参照がなくなるとガベージコレクションされますが、ネイティブの Unity エンジンオブジェクトは、新しいシーンがロードされたとき、または UnityEngine.Object.Destroy() を明示的に呼び出したときに破棄されます。 つまり、破棄されたネイティブオブジェクトを参照するマネージド .NET オブジェクトが存在する可能性があります。
UnityEngine.Object クラスは、 null と比較された際に、基となるネイティブの Unity エンジンオブジェクトが破棄されているかどうかをチェックするカスタム等価演算子を定義します。 つまり、 myMonoBehaviour == null は myMonoBehaviour 変数が割り当てられているかどうか、ネイティブのエンジンオブジェクトが破棄されているかどうかをチェックします。 同様のチェックは、 if (myMonoBehaviour == true)、 if (!myMonoBehaviour) 、単に if (myMonoBehaviour) のようなブール比較でも実行できます。
null 合体演算子または条件演算子を使用すると、意図が曖昧になり、意図したライフタイムチェックが回避される可能性があります。 ライフタイムチェックが意図されていた場合は、 null との明示的な比較、またはブール値の比較を推奨します。 ライフタイムチェックが意図されていなかった場合は、 System.Object.ReferenceEquals() を呼び出して意図を明示してください。 object.ReferenceEquals() の呼び出しはコンパイラーによって単純な null チェックに最適化され、カスタム等価演算子を呼び出すよりも高速であることに注意してください。
null 合体演算子
以下の例では、意図が明確ではありません。 これは、 gameObject 変数が正しく割り当てられているかどうかを確認するためのチェックなのか、それともネイティブの Unity エンジンオブジェクトが破棄されているかどうかを確認するためのチェックなのか、どちらでしょうか?
基となるエンジンオブジェクトのライフタイムをチェックすることが目的だった場合、ライフタイムチェックがバイパスされているため、このコードは誤りです。 明示的な null またはブール値の比較を使用してコードを修正してください。
gameObject 変数が初期化され、有効な C# 参照が割り当てられていることを確認することが目的であれば、 object.ReferenceEquals() を明示的に呼び出すことを推奨します。
これらの変更は若干冗長ではあるものの、意図は明確になった。
null 条件演算子
この例も意図が曖昧です。
繰り返しますが、 monoBehaviour 変数が正しく初期化され、割り当てられていることを単に確認することが目的であれば、 object.ReferenceEquals() を明示的に呼び出すことをお勧めします。
しかし、基となるエンジンオブジェクトの寿命をチェックすることが目的であれば、明示的な null またはブール比較を使用することをお勧めします。
このトピックに関する詳細は、Unity のブログ記事 「カスタム == 演算子、これは残しておくべきでしょうか? 」で確認できます。