null と参照
対応(20)
Section titled “対応(20)”| 書き方 | 説明 | 注意 |
|---|---|---|
string s = null; | 参照型への代入 | |
new string[4] | 生成した直後の配列の要素 | |
new int[4][] | 生成した直後の内側の配列 | |
buffer == null | null との比較 | |
s is null | null との照合 | |
other != null | インタフェースの値との比較 | |
s ?? "x" | null なら別の値 | |
s ??= "x" | null なら代入 | |
target?.name | null なら呼ばない | |
target?.Rotate(...) | null なら呼ばない(戻り値なし) | |
#nullable enable | 検査の有効化 | |
string? s | null になりうる型 | |
#nullable disable | 検査の無効化 | |
s! | null でない扱い | |
if (s != null) { s.Length } | 確認したあとの扱い | |
where T : notnull | null にならない型への制約 | |
[NotNull] | null でないことの指定 | |
[NotNullWhen(true)] out string s | 戻り値が真なら null でない指定 | |
[return: MaybeNull] | null を返しうる指定 | |
[AllowNull] | null を代入してよい指定 |
非対応(4)
Section titled “非対応(4)”| 書き方 | 説明 | エラー | 理由 | 代替 |
|---|---|---|---|---|
c as Transform | 変換できなければ null にする | TUKI0001 | 未実装 | 型が分かっているならキャスト((Transform)c)で書く。union かインタフェースなら型パターンで受ける |
a?[0] | 配列が null なら要素を読まない | TUKI0001 | 環境 | if (a != null) で囲む |
IShape s = new Box(); s == null | 値型に付けたインタフェースを null と比べる | TUKI0001 | 設計 | 値型は null にならない。Behaviour に付けたものなら比べられる |
Point p = new Point(); p == null | struct を null と比べる | CS0019 | 設計 | 値型は null にならない |
| 注釈と属性が有効な時点 | コンパイルの時だけです。? も [NotNull] などの属性も、生成されるプログラムには残りません。エラーにならないため見落としやすい点です |
| 停止する形 | null の参照のメンバを呼ぶ、null の配列の要素を読む。そのイベントはそこで停止し、エラーは実行環境が出します |
| 捕まえられるか | できません。例外の仕組みが無いので、捕まえて続きを実行することはできません |
null の発生源
Section titled “null の発生源”参照型のフィールドと配列の要素は、値を代入するまで null です。初期化子を書かなければ、最初から null が入っています。
参照型への代入
Section titled “参照型への代入”using UnityEngine;using Tsukimi;
public class CvNullLiteral : TsukimiBehaviour{ void Start() { string s = null; Debug.Log(s == null); // => true }}生成した直後の配列の要素
Section titled “生成した直後の配列の要素”using UnityEngine;using Tsukimi;
public class ArraysElementReferenceType : TsukimiBehaviour{ private string[] names;
void Start() { names = new string[4]; Debug.Log(names[0] == null); // => true names[0] = "hello"; Debug.Log(names[0]); // => "hello" }}生成した直後の内側の配列
Section titled “生成した直後の内側の配列”using UnityEngine;using Tsukimi;
public class ArraysDeclareJagged : TsukimiBehaviour{ private int[][] grid;
void Start() { grid = new int[4][]; Debug.Log(grid[0] == null); // => true grid[0] = new int[4]; grid[0][0] = 1; Debug.Log(grid[0][0]); // => 1 }}null の判定
Section titled “null の判定”null かどうかは、参照型の値に対してだけ調べられます。値型は null を取らないので、比較そのものが書けません。
null との比較
Section titled “null との比較”using UnityEngine;using Tsukimi;
public class ArraysCompareToNull : TsukimiBehaviour{ private int[] buffer;
void Start() { Debug.Log(buffer == null); // => true if (buffer == null) { buffer = new int[4]; } Debug.Log(buffer.Length); // => 4 }}null との照合
Section titled “null との照合”== null と同じ判定を、パターンとして書く形です(パターンマッチング)。
using UnityEngine;using Tsukimi;
public class PtConstNull : TsukimiBehaviour{ void Start() { string s = null; if (s is null) { Debug.Log(1); } // => 1 }}インタフェースの値との比較
Section titled “インタフェースの値との比較”null と比べられるのは、Behaviour に付けたインタフェースだけです。
using UnityEngine;using Tsukimi;
public interface IGadget{ void Tick();}
public class IfaceCompareNull : TsukimiBehaviour, IGadget{ public IGadget other;
public void Tick() { }
void Start() { Debug.Log(other == null); // => true if (other != null) { other.Tick(); } }}null を避ける書き方
Section titled “null を避ける書き方”null のときの処理を、if で囲まずに式の中に書く構文です。
null なら別の値
Section titled “null なら別の値”using UnityEngine;using Tsukimi;
public class ExpressionsNullCoalesce : TsukimiBehaviour{ void Start() { string s = null; string r = s ?? "x"; Debug.Log(r); // => "x" }}null なら代入
Section titled “null なら代入”using UnityEngine;using Tsukimi;
public class ExpressionsNullCoalesceAssign : TsukimiBehaviour{ void Start() { string s = null; s ??= "x"; Debug.Log(s); // => "x" }}null なら呼ばない
Section titled “null なら呼ばない”左が null のときは、その先の呼び出しをせずに、式全体が null になります。
using UnityEngine;using Tsukimi;
public class ExpressionsNullConditionalReference : TsukimiBehaviour{ public Transform target;
void Start() { string s = target?.name; Debug.Log(s); // => null }}null なら呼ばない(戻り値なし)
Section titled “null なら呼ばない(戻り値なし)”using UnityEngine;using Tsukimi;
public class ExpressionsNullConditionalVoid : TsukimiBehaviour{ public Transform target;
void Start() { target?.Rotate(Vector3.up); }}null 安全
Section titled “null 安全”| 何をする仕組みか | 参照型を「null になりうる型(string?)」と「ならない型(string)」に分けて、コンパイラが値の流れを追います |
| エラーになる条件 | null になりうる値を確認せずに使うと TUKI0201 でそこでエラーになります。C# のコンパイラは同じ形を警告として報告しますが、この言語ではエラーになり、コンパイルが通りません |
| 有効な範囲 | 既定では無効です。#nullable enable と書いた行から先だけです |
| 有効な時点 | コンパイルの時だけです。できあがるプログラムに検査は残らないので、実行時に null で停止することは防げません |
検査の有効化
Section titled “検査の有効化”#nullable enableusing UnityEngine;using Tsukimi;
public class PreNullableEnable : TsukimiBehaviour{ void Start() { string s = "a"; Debug.Log(s.Length); // => 1 }}#nullable enableusing UnityEngine;using Tsukimi;
public class R_nrt_assign : TsukimiBehaviour{ void Start() { string s = null; Debug.Log(s); }}null になりうる型
Section titled “null になりうる型”型に ? を付けると、null が入りうる値として扱われます。
using UnityEngine;using Tsukimi;
public class NrAnnot : TsukimiBehaviour{ void Start() { #nullable enable string? s = null; Debug.Log(s == null); // => true }}#nullable enableusing UnityEngine;using Tsukimi;
public class R_nrt_deref : TsukimiBehaviour{ void Start() { string? s = null; Debug.Log(s.Length); }}検査の無効化
Section titled “検査の無効化”#nullable enableusing UnityEngine;using Tsukimi;
public class NrDisable : TsukimiBehaviour{ void Start() { #nullable disable string s = null; Debug.Log(s == null); // => true }}#nullable enableusing UnityEngine;using Tsukimi;
public class R_nrt_no_disable : TsukimiBehaviour{ void Start() { string s = null; Debug.Log(s == null); }}null でない扱い
Section titled “null でない扱い”! を付けると、null になりうる値を ? の無い型へそのまま渡せます。
#nullable enableusing UnityEngine;using Tsukimi;
public class PeNullForgiving : TsukimiBehaviour{ void Start() { string? s = Get(); string t = s!; Debug.Log(t); // => "a" }
private string? Get() { return "a"; }}#nullable enableusing UnityEngine;using Tsukimi;
public class R_nrt_no_bang : TsukimiBehaviour{ void Start() { string? s = Get(); string t = s; Debug.Log(t); }
private string? Get() { return "a"; }}確認したあとの扱い
Section titled “確認したあとの扱い”#nullable enableusing UnityEngine;using Tsukimi;
public class NullNarrowing : TsukimiBehaviour{ void Start() { string? s = Get(); if (s != null) { Debug.Log(s.Length); } // => 2 }
private string? Get() { return "ab"; }}#nullable enableusing UnityEngine;using Tsukimi;
public class R_nrt_no_check : TsukimiBehaviour{ void Start() { string? s = Get(); Debug.Log(s.Length); }
private string? Get() { return "ab"; }}null にならない型への制約
Section titled “null にならない型への制約”using UnityEngine;using Tsukimi;
public class GcNotNull : TsukimiBehaviour{ void Start() { Debug.Log(Take<int>(1)); // => 1 }
private int Take<T>(T v) where T : notnull { return 1; }}#nullable enableusing UnityEngine;using Tsukimi;
public class R_nrt_notnull : TsukimiBehaviour{ void Start() { Debug.Log(Take<string?>(null)); }
private int Take<T>(T v) where T : notnull { return 1; }}null でないことの指定
Section titled “null でないことの指定”引数や戻り値が null でないことを C# の解析へ伝えます。
using UnityEngine;using Tsukimi;
public class NrAttr : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }
[System.Diagnostics.CodeAnalysis.NotNull] private string S = "a";}戻り値が真なら null でない指定
Section titled “戻り値が真なら null でない指定”戻り値が真のときは out の値が null でないことを伝えます。
using UnityEngine;using Tsukimi;
public class NrNotNullWhen : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }
private bool Try([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out string s) { s = "a"; return true; }}null を返しうる指定
Section titled “null を返しうる指定”戻り値が null になりうることを伝えます。
using UnityEngine;using Tsukimi;
public class NrMaybeNull : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }
[return: System.Diagnostics.CodeAnalysis.MaybeNull] private string Get() { return null; }}null を代入してよい指定
Section titled “null を代入してよい指定”? の付いていない型にも null を代入できるようにします。
using UnityEngine;using Tsukimi;
public class NrAllowNull : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }
[System.Diagnostics.CodeAnalysis.AllowNull] private string S = "a";}