さけのさかなのブログ

同人ゲーム開発やってます。Unity使ったりする。

Unityでnull許容参照型を使ってみた

Unityでもnull許容参照型。どう書くか好みの別れそうな部分があったので、個人的な方針。

Unity 2021.1.7f1。

1.どう有効にするか

コンパイラオプションでプロジェクト全体を有効にできるけど、それではアセットのコードなんかも影響して警告を出し始めるのでちょっとやってらんない。

対応

ファイルごとに#nullable enableを書く方式でやります。(まあusingだって毎回やってることですし。)

2.Unityのnullチェックでは警告が出る問題

UnityEngine.Objectのnullチェックはコンパイラが追えず、チェック後も警告が出てしまう。

対応

!演算子を使う。

if (hoge) // Unityはnullチェックをこう書けるが
{
    hoge.Piyo(); // 警告が出る。
}

if (fuga)
{
    fuga!.Piyo(); // !演算子を使えば警告は出ない。
}

3.Linqはちょっと不便なところがある

たとえばWhereでnullチェックを行っても、null許容型が非nullになったりはしない。

ついでにToArray()の戻り値はnull許容型。

対応

!演算子を使う。

List<string?> list;
var array1 = list.Where(_ => _ != null).ToArray(); //string?[]?型
var array2 = list.Where(_ => _ != null).Select(_ => _!).ToArray()!; //string[]型

4.GetComponentキャッシュ書き方

Text? text;
Text Text => text ? text! : text = GetComponent<Text>();

5.SerializeField フィールドは非nullにするのが丸い

SerializeFieldの配列やクラスはUnityの仕様上nullにならない。なので非null指定にしてしまうのがよさげ。

対応

default!で初期化する。

[SerializeField]
int[] array = default!;

[SerializeField]
int[] array = new int[0]; // これでも良い。

[SerializeField]
int[] array = default; // これは警告がでる

[SerializeField]
int[]? array = default; // 警告はでないけど、せっかくだから非null指定したい。

6.SerializeField オブジェクト参照で非null指定したいケース

SerializeFieldで参照しているコンポーネントなどがnullかどうかはユーザーの設定次第。よってnull許容にするのが自然。

なんだけど、値が入ってないとエラーにするしかないコアな部分なら、非null指定にしても支障はない。

対応

必須のフィールドなら非null指定にする。

// 動作に必要なフィールドは非nullにしてしまう
[SerializeField]
GameObject prefab = default!;

// nullでも動くならnull許容型にする。
[SerializeField]
AudioClip? sound = default;

7.ジェネリクスはどうしようもない場合がある

参照型か値型かの制限が要る。これはcsharpのバージョンアップを待つことになりそう。

対応

諦めて#nullable disableする。

参考

ufcpp.net