ゲーム開発をする中で「この変数の中身がnullだったらエラー出して欲しいなぁ」という時があり、とりあえず知ってる NullRefereceException を使っていたのですが、使い方を勉強した方がいいなと思う機会ができたため調べたことをまとめていきます
鵜呑みにするのではなく、これも一つの情報源として参考程度にしていただければ幸いです
今回は 参照・null に関連してそうなエラーについてまとめます
使用言語はC#です
エラーと例外
そもそも "エラー" 、 "例外" とはなんでしょうか?
普段何気なく使っている "エラー" という言葉とプログラミングをするとよく耳にする "例外" という言葉ですが、これらに明確な定義というものは存在しないようです
あえて定義をするならば、
システムや環境レベルのどうしようもない失敗
プログラム中で想定して対処できる異常
とするのが私のイメージには近かったです
ちなみにJavaではエラーと例外を別クラスにしているようですが、C#ではほぼ全てExceptionクラスで表現されており、捉え方を実装側に委ねている部分が大きいのではないかと感じました
C#におけるcatchブロック
Microsoft公式サイトにはcatchブロックについて以下のように書かれています
一般に、Exception ブロックでスローされるすべての例外を処理する方法がわかっているか、try ブロックの末尾にthrowステートメントを含めた場合を除き、例外フィルターとしてcatchを指定しないでください
次の条件に該当する場合に例外をキャッチします
- 例外がスローされる理由を十分に理解していて、かつ特定の回復手段を実装できる(FileNotFoundException オブジェクトをキャッチした場合に、ユーザーに新しいファイル名を入力するよう求めるなど)
- より具体的な例外を新規に作成し、スローできる
公式サイトから分かるように、C#では異常事態をほぼ全て「例外」という言葉で表現しているようです
引用に関して簡単にいえば「catch使うならちゃんと対処してね」ってことなのですが、これを踏まえてC#で 概念としてのエラーと例外 を使い分けるとするならば、throwされたExceptionクラスをcatchした際に しっかりと対策できるものは例外 、 できないものはエラー とするのがいいのかもしれません
言語レベルで扱い方が違うみたいですが、会話の中で使うには十分伝わりますし、言い方についてはそこまで気にしなくていいかもしれません
これ以降はMicrosoft公式サイトに合わせて "例外" という言い方で進めていきます
参照・nullに関連する例外
例外にはかなりの種類がありますが、その中でも以下が参照・nullに関連していそうなものとして挙げられます(他にもあるかもしれません)
- NullReferenceException
- ArgumentNullException
- InvalidOperationException
例外の扱い方
上記の例外の扱い方を見ていきましょう
NullReferenceException
null参照時にランタイム1から投げられる例外です
プログラム2で明示的に例外を投げることもできますが、基本的にはランタイムから投げられることを待ちましょう
とはいえ、そもそもこの例外が投げられないようなプログラムを書くべきだということを意識した方がいいかと思います
_instance.DoSomething(); // _instanceがnullの場合に例外が投げられる
_instance?.DoSomething(); // _instanceがnullの場合はメソッドを実行しない
if (_instance == null)
throw new NullReferenceException(); // _instanceがnullなら例外を投げる
_instance.DoSomething();
ArgumentNullException
メソッドの引数に渡された参照がnullだった場合にランタイムから投げられる例外です
自分で実装したメソッドの中で明示的に投げることでプログラムの制御をすることもできます
int.Parse(null); // nullに対して整数に変換する処理を行おうとした
void DoSomething(SampleClass arg)
{
if (arg == null)
throw new ArgumentNullException();
// SampleClassクラスの引数argにnullが渡された時に例外を投げる
}
InvalidOperationException
オブジェクトの現在の状態が不正だった場合にランタイムから投げられる例外です
(ここでいう「オブジェクト」はクラス(参照型)を想像していただければ問題ないです)
「この時にオブジェクトの状態がおかしかったら先には進められない」という時に投げることでプログラムの制御をすることもできます
参照がnullな場合にプログラムから投げる例外の中では一番汎用的で使いやすいものだと思いますが、むやみやたらに使わずに適した状況下でのみ投げるように意識しましょう
冒頭に関して、私はNullReferenceExceptionではなくこの例外を投げるべきでした
var list = new List<int> { 1, 2, 3 };
foreach (var x in list)
{
list.Add(4); // コレクションの列挙中にコレクションを変更したことによる例外
}
if (!_isInitialized)
throw new InvalidOperationException();
// 初期化されていない場合に先に進めると壊れるため例外を投げる
最後に
今回は参照・nullに関連してそうな例外についてまとめました
例外は他にもたくさんあるため、もし例外を扱ってみたい場合は調べてみるといいと思います
せっかく作ったものを壊さないように、例外は必ず 適切なタイミング で 適切な例外 を投げるようにしましょう