Nullable reference typesの境界の跨ぎ方
C#は8.0でNullable reference typesが加わりましたが、Unity向けのコードなど、依然としてNullable reference typesを有効にできない環境があります。Nullable reference typesが有効なコンテキストのコードとNullable reference typesが無効なコンテキストなコードの境界でのデータのやりとりには追加の注意が必要になります。
以下、Nullable reference typesが有効な環境を #nullable enable
, Nullable reference typesが無効な環境を #nullable disable
といいます。
#nullable disable
の string
!= #nullable enable
の string?
#nullable enable
では以下は型チェックに失敗します。
// #nullable enableなコード
void Hoge(string arg) {}
string? hello = ...;
Hoge(hello);
#nullable enable
では、単に string
としたら そこにnullは入り得ない ことが期待され、そのように型チェックされるため、 string?
な hello
を Hoge
の引数として与えることはできません。
一方、以下はどうなるでしょうか。
// #nullable enableなコード
void Hoge(string arg) {}
// #nullable disableなコード
string hello = ...;
Hoge(hello);
これは コンパイルが通ってしまいます。 #nullable disable
なので hello
にはnullが 入り得る のですが、それを #nullable enable
な関数 Hoge
への string
型の引数として与えることができてしまいます。
C#は、 #nullable disable
から #nullable enable
へのデータの受け渡し時に、その値が #nullable enable
での値域にあることの保証を プログラマに委ねています。 これを守らなかった者に与えられるのは起き得ないはずの例外です。
// #nullable enableなコードのHogeの実装例
void Hoge(string arg)
{
// #nullable enableなので、argがnullであることは考慮しなくていい (するべきではない)
// したがってLengthを取ることは常に安全で例外が飛ぶことがない
if (arg.Length > 5)
{
// ...
}
}
// #nullable disableなコード
string hello = null;
Hoge(hello);
Pitfalls
C#は鉄壁の安全性を誇る言語ではないので、このような境界を跨ぐ場合以外にも非nullableな型の変数にnullが入りうるケースがあります。
// #nullable enableなコード
struct Hello
{
string World;
}
Hello data = default;
このコードは コンパイルが通ります。 そして data.World
は null
を指します (structのゼロ初期化のため)。この例は 公式ドキュメントのPitfalls に挙げられています。
Discussion