ArgumentNullException と NullReferenceException は、どちらも「null」が絡む例外だが、意味も出し方も違う。
現場では「どっちを投げればいい?」「どっちが落ちた?」で迷いやすいので、ざっくり使い分けを整理する。
ArgumentNullException … 呼び出し側が渡した引数が null。メソッドの入り口で明示的に投げるNullReferenceException … null の参照にメンバーアクセスした。実行時にフレームワーク/ランタイムが投げることが多いArgumentNullException(または ArgumentNullException.ThrowIfNull)ArgumentNullException = 「お前が渡した null が悪い」(契約違反・呼び出し側の責任)NullReferenceException = 「null のまま触った」(多くの場合、実装側の想定漏れ)メソッド/コンストラクタの引数が null だと先に進めないとき。入り口で落とす。
public void Send(string to, string body)
{
ArgumentNullException.ThrowIfNull(to);
ArgumentNullException.ThrowIfNull(body);
// ...
}
古い書き方:
if (to is null)
throw new ArgumentNullException(nameof(to));
null 参照に対して . でメンバーアクセスしたとき。自分で throw new NullReferenceException() することはほぼない(してはいけない寄り)。
User? user = FindUser(id); // 見つからず null
Console.WriteLine(user.Name); // NullReferenceException
詳細な発生パターンは NullReferenceException の記事 を参照。
「必須のはずの取得が null」なら、InvalidOperationException や独自例外の方が意味が伝わることが多い。NullReferenceException を自分で投げる必要はない。
var user = repo.Find(id)
?? throw new InvalidOperationException($"User {id} was not found.");
ガードせずにそのまま触ると NRE になる。呼び出し側から見ると「契約違反なのに NRE」で分かりにくい。公開 API なら入り口で ArgumentNullException。
string(非 null)引数なのに実行時 null が来る → 入り口で ArgumentNullExceptionstring? を許すなら、使う直前で分岐する(NRE を出さない)Parameter name: xxx)がある → ArgumentNullException。呼び出し元の渡し方を疑うfoo.Bar のようなメンバーアクセス → NRE。その行の左辺が nullArgumentNullExceptionNullReferenceException(多くの場合バグ)