C#には少し特殊な型でNullable型と呼ばれる型が存在します。
値型と一緒に利用する特殊な型で、知っておくと便利系な知識です。
Nullable型(null許容値型)
これは値型を拡張してnull値の代入を可能にする仕組みです。
こんなのをイメージしてください。値型をNullable型で包み込む感じです。
![Nullable型](https://takemi.blog/wp-content/uploads/2024/04/744E6523627749D388782016A15467C9_0001.webp)
こうやって元の値型の機能にNullable型の機能を付加します。
なので、1段階経由して値型にアクセスしてる感じですね。
![](https://takemi.blog/wp-content/uploads/2023/09/00D90ECEDDFA45078397DE4A2A2F7549.webp)
あくまでイメージの話ね。後、普通の値型と比べてパフォーマンスが落ちます。
![](https://takemi.blog/wp-content/uploads/2024/03/D46ED1C42500466BB89D4AD9CC80048F.webp)
気にするほど?
![](https://takemi.blog/wp-content/uploads/2023/09/00D90ECEDDFA45078397DE4A2A2F7549.webp)
いや、どうせ誤差だよ。
宣言方法
値型に?を付けて宣言するとNullable型になります。
Nullable型になると通常の値以外にnullも代入できるようになります。
int? x = null;
以下はサンプルコードです。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
// Nullable型
int? x = null; // nullを代入
int? y = 100; // 数値を代入
Console.WriteLine($"x={x}, y={y}");
}
}
}
![](https://takemi.blog/wp-content/uploads/2024/03/D46ED1C42500466BB89D4AD9CC80048F.webp)
これ何に使うんですか?
![](https://takemi.blog/wp-content/uploads/2023/09/00D90ECEDDFA45078397DE4A2A2F7549.webp)
値型だけど「Default値は未定義が良いなぁ」って時とか。
値型はDefault値で初期化されるため未定義がありえません。
例としてint型のDefault値は0なので、少なくとも0という値が入ってます。
よくあるパターンでint型の-1を未定義として扱い、正数をまともな値として利用することがあります。
そんな時、「冷静にそれ違くね?」って思ったらNullable型がベストだったりします。
つまり、値型だけど値が存在しない場合を表現したい時に使うのがオススメです。
HasValueプロパティ
英語のままで値の存在有無を取得できます。型はbool型です。
つまり、この値を確認すればnullor値を判断できます。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
// Nullable型
int? x = null; // nullを代入
int? y = 100; // 数値を代入
Console.WriteLine($"x.HasValue={x.HasValue}, y.HasValue={y.HasValue}");
}
}
}
また、HasValueを直接比較してもOKですが、実はNullable型を直接null判断しても動作します。
以下がサンプルコードです。if文やis演算子で直接比較した場合でも結果は同じになります。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
int? x = null;
// HasValue
if (x.HasValue)
Console.WriteLine("x is not null");
else
Console.WriteLine("x is null");
// null比較
if (x != null)
Console.WriteLine("x is not null");
else
Console.WriteLine("x is null");
// is演算子
if (x is not null)
Console.WriteLine("x is not null");
else
Console.WriteLine("x is null");
}
}
}
Valueプロパティ
これも英語のままです。Nullable型にした値型の、値自体を取得できます。
ただし、null状態でアクセスするとInvalidOperationExceptionが発生します。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
// Nullable型
int? x = null; // nullを代入
int? y = 100; // 数値を代入
try
{
// nullは例外が発生する
Console.WriteLine($"x.Value={x.Value}");
}
catch (Exception e)
{
Console.WriteLine($"{e.Message}");
}
// null以外なら値が取得できる
Console.WriteLine($"y.Value={y.Value}");
}
}
}
これもHasValueと同じで、直接アクセスすることができます。
以下がサンプルコードです。最初に例として紹介したコードと同じです。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
// Nullable型
int? x = null; // nullを代入
int? y = 100; // 数値を代入
Console.WriteLine($"x={x}, y={y}");
}
}
}
また、is演算子を併用すれば、値が存在する場合だけ処理するような記述ができます。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
int? x = 10;
// 値があるときだけ処理する
if (x is var v)
Console.WriteLine($"{v}");
}
}
}
Valueプロパティを経由しない場合の注意ですが、あくまで型はNullable型です。
忘れがちになりますが、次のような記述はもちろん無理です。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
int? x = 10;
// 型が異なるのでNG
Method(x);
}
static void Method(int v)
{
Console.WriteLine($"{v}");
}
}
}
GetValueOrDefault()メソッド
これはNullable型がnullなら値型のDefault値、null以外なら所有する値を取得するメソッドです。
稀の稀に利用することがあるかもしれません。僕は経験上、殆ど使った記憶がないです。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
int? x = null;
int? y = 100;
Console.WriteLine($"x.GetValueOrDefault()={x.GetValueOrDefault()}, y.GetValueOrDefault()={y.GetValueOrDefault()}");
}
}
}
![](https://takemi.blog/wp-content/uploads/2023/09/00D90ECEDDFA45078397DE4A2A2F7549.webp)
Nullable型はnullであること意味があるので、これを使うようなら値型に戻したほうがいいよ。
リフト演算子
これはNullable型に対して単項演算子(++や--等)と2項演算子(+とか-等)を利用した場合に機能します。
先に言ってしまうと、Nullable型がnullの状態で演算しても、良い感じに処理して大体nullになる演算子です。
大体と言ったとおり、時々違います。特にbool型がスーパー迷惑で分かりづらいです。
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
int? x = 10;
int? y = 25;
int? z = null;
Console.WriteLine($"x + y = {x + y}");
Console.WriteLine($"x + z = {x + z}");
Console.WriteLine($"x++ = {++x}");
Console.WriteLine($"z++ = {++z}");
bool? a = null;
Console.WriteLine($"a & true = {a & true}");
Console.WriteLine($"a & false = {a & false}");
Console.WriteLine($"a | true = {a | true}");
Console.WriteLine($"a | false = {a | false}");
}
}
}
![](https://takemi.blog/wp-content/uploads/2023/09/00D90ECEDDFA45078397DE4A2A2F7549.webp)
正直、これは覚えなくていいです。むしろ間違いの元だから使わないべき。
![](https://takemi.blog/wp-content/uploads/2024/03/D46ED1C42500466BB89D4AD9CC80048F.webp)
ぱっとみ分かりづらいね。
特に2項演算子の場合ですが、そもそも普通の値型とNullable型を直接演算するのをやめましょう。
仮に演算したい場合はValueプロパティを経由します。もしくはis演算子でnullを捌きます。
そうすればnull演算は発生しないため、先程のリフト演算子の挙動を気にする必要がなくなります。
あとがき
正負の数値を扱うけど未定義もありえるとか、そんな時はNullable型が向いてます。
後はNull条件演算子とか、少し特殊な演算子と一緒に利用することが多いですね。
ここではC#のnullに対する便利系の演算子、Null条件演算子とNull合体演算子を学びます。知らなくてもOKと言いたいとこですが、これ超便利です。僕は必須知識だと思ってます。Null条件演算子Null条件演算子とは、メソッド等にアクセスする際、対象がnullの場合でも良い感じに処理してくれる演算子です。まずは次のサンプルコードを確認しましょう。これは普通にクラスのメソッドを呼んでるだけです。namespace Sample { internal class Pr...
◆ C#に関する学習コンテンツ
この記事は参考になりましたか?
コメント