C#
Unity

UnityのGetCompornent<T>()でNull条件演算子が使えない

2018.3でC#6が昇格(?)して、便利になりました。

しかし

var r = GetComponent<Rigidbody>();

if(r != null)
{
// Rigidbodyがアタッチされていないとここは通れらない
r.AddForce(force, mode);
}

// 一方で・・・

GetComponent<Rigidbody>()?.AddForce(force, mode);
// MissingComponentException発生!


ゲームオブジェクトに type がアタッチされている場合は type のタイプを使用してコンポーネントを返します。ない場合は null です

https://docs.unity3d.com/ja/current/ScriptReference/Component.GetComponent.html


なんでやねん!

どうもGetComponent<T>()で帰ってくるnullは真正のnullではないみたいです。デバッカで見ると{null}となっています。

発生している例外もNullRefarenceExceptionではありません。


C# 6で導入されたnull条件演算子(?.)ですが、以下の2つの式がほぼ同じ意味になります。

x != null ? x.M() : null

x ?.M()

「ほぼ」であって「完全に同じ」と言えないのは、==演算子を呼ぶか呼ばないかが変わってしまうせいです。 前者(自分で==を呼んでいるやつ)はオーバーロードされた==を呼び出しますが、 後者(?.を利用)は呼びません(直接nullかどうか調べます)。

https://ufcpp.net/blog/2016/12/tipsnulloperation/


==がオーバロードされているのかもしれません。


(追記)どうしてもnull条件演算子が使いたい!!

UnityObject版null -> 正真正銘のnullに変換する操作を拡張メソッドで隠蔽する方法があるそうです。

本来のnull条件演算子だけに比べると少し長くなってしまいますが、それでもif(component!=null)で囲むよりは幾分か短く書くことが出来ます。

public static T NullCast<T>(this T obj) where T : UnityEngine.Object

=> (obj != null) ? obj : (System.Object)null;

GetComponent<Rigidbody>().NullCast<Rigidbody>()?.AddForce(force, mode);

詳細は@albireoさんのコメントを参照。(ありがとうございました!🙇🙇🙇)


(追記)参考、この辺に詳しい話が書かれてますね

https://ufcpp.net/study/csharp/rm_nullusage.html#user-defined-null