.NET 2で初めて導入されたNullable<T>
は、過去10年半にわたってほとんど変更されなかったが、間もなく変更されるようだ。C# 8では、null許容の参照型をサポートする操作の多くが、null許容の値型にも適用できるようになる。
最初に挙げるのは、"null state tracking"プロポーザルだ。これによれば、必要なnullチェックが実行されていないために失敗する可能性のあるコードを、コンパイラが検出できるようになる。null許容の参照型の場合と同じように、安全でない方法で使用されたNVTを、コンパイラが警告する。この機能をサポートするために、Nullable<T>.HasValue
メソッドが、他のnullチェックと同様に扱われるようになる。
さらに難しいのは、nullでないことが分かっている場合、null許容値を通常の非null許容値として使用できるようにするかどうか、という問題だ。これが実現すれば、.Value
や.GetValueOrDefault()
といったメンバを使用する必要はなくなる。プロポーザルは次のように続く。
当面の問題のひとつは、NVTそれ自体が型であって、独自のメンバと、独立したタイプID書き込みオーバーロード解決を持っていることです。メンバアクセスと変換に関しては、すでにセマンティクスを持っているので、下位の型として"ポーズ"するような新しいセマンティクスは、厳密に追加的であって、既存のコードを毀損しないものでなくてはなりません。そのため、現在エラーが発生する場所でのみ、動作することになります。
単純化の面からは、値がnullであるかどうかに関係なく、新たなメンバアクセスや変換を可能にする必要があると思われますが、そのようにすると、"nullかも知れない"null状態を持つnull許容値に適用された場合、警告が発生することになります。null状態に関係なく許可することは、null状態がセマンティクスには影響せず、警告の発生にのみ影響するという、NRTの設計原則の維持においても有効です。
この機能を採用する場合には、いくつかのメソッドをシャドウイングする必要がある。
後方互換性のため、Nullable<T>で定義されているメンバは、基になる型の対応するメンバを常にシャドウしなければなりません。ただし、この対象は非常に少ない上、その多く(ToString、Equals、GetHashCode)は、値がnullでない場合に下位タイプを呼び出すことによって動作するものです。従って、下位値のごく少数のメンバが、動作が異なるという感覚に従って効果的にシャドーされることになります。これに当てはまるのは、NVTのコントラクト(Value、HasValue、GetValueOrDefault)を直接実装したメンバやリフレクション(GetType)で、null許容値が下位の値と異なることを実行時に認識する必要のあるものたちです。
最終的にはこれが、構造体は独自のValue、HasValue、GetValueOrDefaultメンバを実装してはならない、という設計ガイドラインにつながっている。
現時点では、null trackingを実装して、C#の最新バージョンまで後者の機能を延期することが推奨されている。