.NET Frameworkの参照型では、値が未設定であることを表すためにnull
/Nothing
を使用することができます。 一方int
/Integer
などの値型では、null
を代入することはできません。 そのため、未設定や無効な状態であることを表すために0
(あるいは-1
や最大値などの値)を代入しておくという手法をとる場合があります。 しかし、0
という具体的な値を使用してしまうと、未設定であるために0
なのか、あるいは0
という有効な値が代入されているのか、これら二つの状態があいまいになるという問題があります。
こういった問題を解消するため、値型に対してもnull
を代入できるようにしたものがヌル許容型(Nullable型)です。 ヌル許容型を使用すると、値型でもnull
が代入されている状態を作り出すことができ、これにより具体的な値が設定されていない状態や無効な状態などを表現することができます。
ヌル許容型に関連して、以下のような演算子・修飾子が導入されています。 詳細については後述しますが、各記号の使い分けやヌル許容型の要点として以下の表をご覧ください。
名前 | 記号と使用例 | 機能・動作 |
---|---|---|
Null許容修飾子 |
int? n
List<int?> list
Dim n As Integer?
Dim list As List(Of Integer?)
|
値型のヌル許容型化
型をヌル許容にする、ヌル許容型を宣言する |
Null合体演算子 |
x = n ?? 3
|
ヌル許容型・参照型の非null化
値を参照して、nullの場合は非null値を設定する |
Null条件演算子 |
(list = nullとして)
len = list?.Length
arr = list?.ToArray()
len = list?.ToArray()?.Length
val = list?[0]
|
null参照のショートサーキット
インスタンスや戻り値がnullの場合に、後続するメンバ呼び出しの結果をnullにする |
ヌル許容型
ヌル許容型の宣言
ヌル許容型を宣言する場合は、型名の後ろにクエスチョンマーク?
を付けます。 例えばC#のint型ならint?
、VBのInteger型ならInteger?
のようになります。 ヌル許容型int?
では、ヌル許容でないint
(ヌル非許容型)に対して代入できる値に加えて、null
/Nothing
を代入することができるようになります。 ヌル許容型で型名の後ろに付けられる?
は、Null許容修飾子と呼ばれます。
整数型などの基本型だけでなく、任意の値型をヌル許容型にすることができます。 独自に定義した構造体の場合も同様に、Null許容修飾子?
を付けるだけでヌル許容型として宣言することができます。
構造体をヌル許容にする場合に関して、§.ヌル許容型構造体でのフィールド・プロパティの値の変更も参照してください。
ヌル許容型の配列・コレクション
ヌル許容型は配列やList<T>などのコレクションでも用いることができます。 int?[]
やList<int?>
といったように、配列・コレクションの型にNull許容修飾子?
をつければ、ヌル許容型の配列・コレクションを作成することができます。
配列やコレクションで複数の値を扱う際、要素の一部に空の状態(empty)や未設定の状態(undefined, uninitialized)を設定したい場合があります。 こういった場合、値型ではnull
を用いることができないため、例えば0
や-1
などに特別な意味を持たせる場合がありました。 しかし、0
や-1
に空や未設定などの意味を持たせても、それを認識していなければ単なる数値であることにかわりなく、意味が無視され他の数と同列に処理されてしまう可能性があります。 ヌル許容型を用いれば空や未設定の状態を表すためにnull
を用いることができるようになり、左記のような問題を避けることができます。
この例ではListの初期化にコレクション初期化子を用いています。 コレクション初期化子を用いたインスタンスの作成についてはジェネリックコレクション(1) List §.コレクション初期化子を参照してください。
VBでヌル許容型の配列を宣言する場合、Null許容修飾子?
を付ける位置に注意する必要があります。 Null許容修飾子?
と、配列を表す配列修飾子()
は常にひと組で記述する必要があります。 変数の側に配列修飾子()
を付け、型名の後にNull許容修飾子?
を付けたり、またその逆に付けるとコンパイルエラーとなります。
値の設定状態のテスト
等号演算子・Is演算子によるテスト
ヌル許容型に有効な値(null
/Nothing
以外の値)が設定されているかどうかを調べるには、==
演算子, !=
演算子(VBではIs
演算子, IsNot
演算子)を使ってnull
/Nothing
と比較します。
HasValueプロパティによるテスト
null
/Nothing
との比較の他に、HasValueプロパティをチェックする方法もあります。 ヌル許容型に値が設定されている場合、HasValueプロパティはtrue
になります。
参照型の場合とは異なり、ヌル許容型変数にnull
が設定されている場合にHasValueプロパティを参照しても、ヌル参照(NullReferenceException)にはなりません。 ヌル許容型にnull
が設定されている(値が設定されていない)場合、HasValueプロパティはfalse
になります。
値の参照・ヌル非許容型への変換
ヌル許容型から値を取り出してヌル非許容型に代入するには、ヌル非許容型への明示的な型変換を行うか、Valueプロパティを参照します。 明示的な型変換を行う場合、拡大変換となる型変換(int
→long
、byte
→int
など)であれば型の異なるヌル非許容型への代入を行うこともできます。
Valueプロパティは取得専用のため、このプロパティを使って値を設定することはできません。 ヌル許容型への値を設定は、通常の変数と同様に直接代入して行います。
値が設定されていない状態で、ヌル非許容型へのキャストまたはValueプロパティを参照すると例外InvalidOperationExceptionがスローされます。 (NullReferenceExceptionではないので注意)
Null合体演算子・If演算子による値の非null化
C#では値の取り出しにNull合体演算子??
を使用することもできます。 この演算子は、ヌル許容型の持つ値を参照する際、値がnull
だった場合に代替として用いる非null値を指定する演算子です。 三項演算子?:
と似ていますが、よりシンプルに記述できます。 例えばヌル許容型変数a
に対してx = a ?? 16
という式を記述した場合、a
が値を持っている場合はその値、持っていない場合は16
がx
に代入されます。
VBではこれに相当する演算子は用意されていませんが、二項形式のIf
演算子を使用することで同様のことができます。 (詳細:論理演算子 §.If演算子)
Null合体演算子を用いることにより、値がnull
だった場合に別の値を代入するといった操作を、if文による条件分岐や三項演算子?:
を用いずに記述することができます。
これと同様の操作は後述のGetValueOrDefaultメソッドを使うことによっても行うことができます。
参照型でのNull合体演算子の使用
Null合体演算子は参照型に対しても用いることができます。 ヌル許容型に対して用いる場合と同様、第一項がnull
だった場合は第二項の値が用いられます。 VBの二項形式のIf演算子も参照型に対して同様に動作します。
Null合体演算子と他の型への変換
Null合体演算子では、第一項・第二項ともに同一の型である必要があります(Option Strict Onの場合はVBのIf演算子も同様)。 例えば、次の例のようにヌル許容型の値を型変換するような場合、null
だった場合の値をにはNull合体演算子単体では処理できません。 この場合はif文や三項演算子を用いるか、あるいはNull条件演算子を用います。
Null条件演算子については§.Null条件演算子で別途解説します。
GetValueOrDefaultメソッドによる値の非null化
Null合体演算子に似たものとして、GetValueOrDefaultメソッドがあります。 このメソッドは、ヌル許容型に値が設定されている場合はその値を、設定されていない場合は型のデフォルト値(0
または0
に相当する値)を返します。 0
以外の値をデフォルト値としたい場合は、引数でデフォルト値として使用する値を指定することもできます。 戻り値はヌル非許容型となるため、デフォルト値としてnull
/Nothing
を指定することはできません。
このメソッドは、ヌル許容型に値が設定されていない場合はデフォルト値を使って処理を継続させたい場合などに使用することができます。
型のデフォルト値については型の種類・サイズ・精度・値域 §.型のデフォルト値を参照してください。
デフォルト値をnull
として処理を継続させたいような場合は、Null条件演算子を使うことができます。
ヌル許容型での演算
ヌル許容型に対しても、ヌル非許容型での演算と同様に加算などの演算を行うことができます。 ただし、演算子の項のどちらか一方がnull
の場合は、演算結果もnull
となります。 これに従い、演算結果も必然的にヌル許容型となります。 そのため、ヌル許容型を項に含む演算結果をヌル非許容型に代入することはできません。
ヌル許容型でのToStringメソッドによる文字列化
ヌル許容型ではToStringメソッドを呼び出すことはできますが、引数で書式を指定することはできません。 これは、ヌル許容型を構成するNullable<T>構造体のToStringメソッドには書式を指定するバージョンがないためです。 書式と同様、カルチャなどの書式プロバイダを指定することもできません。
そのため、書式を指定してヌル許容型の値を文字列化したい場合は、String.Formatメソッドを使うか、Valueプロパティを参照してその値に対してToStringメソッドを呼び出す必要があります。
Nullable<T>構造体については後述の§.Nullable<T>構造体を参照してください。
書式を指定した文字列化に関しては書式指定子、書式プロバイダについてはカルチャと書式・テキスト処理・暦または書式の定義と実装を参照してください。
ヌル許容型構造体でのフィールド・プロパティの値の変更
次の例のように、ヌル許容型の構造体でフィールド・プロパティの値を変更しようとした場合、コンパイルエラーとなります。
このような場合、一旦ヌル非許容の一時変数を使って変更、再代入を行う必要があります。
このようにする必要がある理由については値型と参照型 §.値型のプロパティ・インデクサで詳しく解説しています。
Null条件演算子
ヌル許容型や参照型のメソッドを呼び出す場合など、ヌル参照を避ける目的で次のように事前にif文などによるチェックを行う場面が多くあります。 C# 6.0およびVisual Basic 2015以降ではNull条件演算子が新たに導入されていて、このようなヌルチェックの処理をシンプルに記述することができます。
このようにNull条件演算子は、プロパティ参照やメソッド呼び出しなど、メンバへのアクセスを行おうとした場合にnull
かどうかのチェックを行います。 この時、null
だった場合は呼び出しを行わず、null
を結果として返します。 Null条件演算子を記述した場合でも、呼び出し元(Null条件演算子の左項)がnull
でなければ通常のメンバアクセスと同様に扱われます。
またNull条件演算子を使う場合、その結果はメンバへのアクセスによって得られる値(プロパティの値・メソッドの戻り値)か、あるいはnull
のどちらかになります。 そのため、必然的にNull条件演算子の左辺は参照型あるいはヌル許容型となります。
Null条件演算子は、メソッド・プロパティ・インデクサなどの参照に前置することができます。 また、戻り値を持たないメソッドの呼び出しにも使うことができます。 ただし、インデクサの設定に用いることはできません。
さらに、メソッドチェイン(戻り値から連続するメソッド呼び出し)においてもNull条件演算子を用いることができます。 この場合、呼び出し元や戻り値がnull
となった時点で結果はnull
に確定し、それ以降の呼び出しが行われなくなります(ショートサーキット)。
演算子によってnull
を後続に伝播させることができるとも捉えることができるため、Null条件演算子の導入前はNull伝播演算子(null propagating operator)とも呼ばれていました。
このほか、イベントの発生にもNull条件演算子を用いることができます。 イベントの発生は、Null条件演算子によって記述がシンプルになる好例です。
イベントの発生についてはイベント、INotifyPropertyChangedとPropertyChangedEventHandlerによるプロパティ変更の通知についてはプロパティ §.プロパティ変更の通知 (INotifyPropertyChanged)で解説しています。
Nullable<T>構造体
ヌル許容型の実体はNullable<T>構造体です。 そのため、次のようにNullable構造体を使ってヌル許容型の変数を宣言することもできます。 ヌル許容型のValueやHasValueなどのプロパティはNullable構造体によって提供されます。
言語が直接ヌル許容型をサポートしていない場合でも、ジェネリクスを使用できる言語であればNullable構造体を使用してヌル許容型を作成・使用することができます。
Nullable構造体の型引数T
には値型のみを指定できます。 従って、参照型をベースにしたヌル許容型を作成することはできません。
また、Nullable構造体を使ってヌル許容型のヌル許容型を構成するといった入れ子にすることもできません。
ヌル許容型と型情報
ヌル許容型でGetTypeメソッドを使用して型情報を取得しようとすると、ヌル許容型の元になった型の型情報が返されます。 例えばint?
ではSystem.Nullable<System.Int32>
ではなくSystem.Int32
が返されます。
逆に、typeof
演算子・GetType
演算子によって型情報を取得する場合、System.Int32
ではなくSystem.Nullable<System.Int32>
が返されます。
またC#では、ヌル許容型をis
演算子で比較する場合、元の型が同じであればヌル許容型でもヌル非許容型でも同一の型とみなされます。
このように、is
演算子では方がヌル許容型かヌル非許容型かを区別することができません。 ヌル許容型かどうかを調べる方法については後述の§.型あるいは値がヌル許容型かどうかを調べるで解説します。
型あるいは値がヌル許容型かどうかを調べる
C#・VBでは、型がヌル許容型かどうかを調べる手段は言語の機能としては用意されていません。 代わりに、Nullable.GetUnderlyingTypeメソッドを使って調べることができます。
Nullable.GetUnderlyingTypeメソッドは、ヌル許容型のベースとなる型の型情報(System.Type)を返します(例えばint?
ならint
)。 ヌル許容型でない場合、このメソッドはnull
を返すので、これによって型がヌル許容型かそうでないかを判別することができます。
§.ヌル許容型と型情報でも述べているとおり、ヌル許容型でのGetType()メソッドによる型情報の取得やis演算子
による比較では、ベースとなっている型情報に対して行われるため、これを用いてヌル許容型かどうかを調べることができません。 値(インスタンス)がヌル許容型かどうかを調べるには、いくつか方法が考えられます。
ひとつは、以下のようなジェネリックメソッドを用意し、型引数Tに対してNullable.GetUnderlyingTypeを呼び出すことでヌル許容型かどうかを判別する方法です。 ただし、この方法ではObject
型に代入されたヌル許容型の値は、ヌル非許容型として判別されます。
この他にもc# - How to check if an object is nullable? - Stack Overflowにて様々な手法が掲載されています。
Nullable<T>のデフォルト値
default(T)
やnew
によって作成されるデフォルト状態のNullable<T>(Nullable<T>の規定値)は、HasValueがfalse
であり、null
が設定されている状態と等しくなります。
ヌル非許容の参照型
値型ValType
におけるヌル許容型ValType?
に対して、参照型RefType
をヌル非許容にした型RefType!
のようなものは現時点では用意されておらず、作成することはできません。 ヌル非許容型を使用したい場合は、struct
によって値型とするか、契約プログラミングの手法を用いるなど、他の手段によってnull
を非許容とする制約を施すほかありません。