ArgumentNullExceptionのスローはどのように役立ちますか?


27

メソッドがあるとしましょう:

public void DoSomething(ISomeInterface someObject)
{
    if(someObject == null) throw new ArgumentNullException("someObject");
    someObject.DoThisOrThat();
}

を投げることArgumentNullExceptionは「正しい」と信じるように訓練されていますが、「オブジェクト参照がオブジェクトのインスタンスに設定されていません」エラーはバグがあることを意味します。

どうして?

参照をキャッシュsomeObjectして後で使用する場合は、渡されたときに無効性をチェックし、早期に失敗する方が良いことを知っています。ただし、次の行でそれを間接参照している場合、なぜチェックを行うことになっていますか?何らかの方法で例外をスローします。

編集

それはちょうど私に起こりました...逆参照されたnullの恐怖は、あなたをチェックしないC ++のような言語に由来しますか?


例外を明示的にすると、エラーの可能性があることを確認するのに役立ちます。エラーはドキュメントに直接記載されています。
zzzzBov

回答:


34

変数をすぐに参照解除する場合は、どちらの方法でも議論できると思いますが、ArgumentNullExceptionの方が好きです。
何が起こっているかについて、より明確です。例外にはnullであった変数の名前が含まれていますが、NullReferenceExceptionには含まれていません。特に、APIレベルでは、ArgumentNullExceptionにより、一部の呼び出し元がメソッドのコントラクトを尊重しなかったこと、および失敗が必ずしもランダムエラーまたは何らかのより深い問題ではないことが明らかになります(ただし、それでも可能性があります)。早く失敗するはずです。

さらに、後のメンテナーがする可能性が高いのは何ですか?最初の間接参照の前にコードを追加する場合はArgumentNullExceptionを追加しますか、それとも単にコードを追加してからNullReferenceExceptionをスローできるようにしますか?


拡張により、これが非パブリックメソッド、または非パブリッククラスのパブリックメソッドである場合、nullチェックの正当な理由はないと思いますか?
スコットホイットロック

通常、これらのチェックをプライベートメソッドに追加しませんが、一貫性を保つためにプライベートクラスのパブリックメソッドに追加することがあります。プライベートメソッドの場合、パブリックコールレベルで引数がより早く検証されると仮定する傾向があります。
マットH

54

ArgumentNullExceptionのスローは「正しい」と信じるように訓練されていますが、「オブジェクトのインスタンスに設定されていないオブジェクト参照」エラーは、バグがあることを意味します。どうして?

M(x)あなたが書いたメソッドを呼び出すとします。nullを渡します。名前が「x」に設定されたArgumentNullExceptionを受け取ります。この例外は明確に、バグがあることを意味します。xにnullを渡すべきではありません。

あなたが書いたメソッドMを呼び出すとします。nullを渡します。null deref例外が発生します。どのように一体私がいるかどうかを知ることになっています、私はバグを持っているか、あなたはバグか持っているあなたが私に代わって呼び出されることを、いくつかのサードパーティのライブラリがバグを持っていますか?私は自分のコードを修正する必要があるのか​​、それとも修正するようにあなたに電話する必要があるのか​​について何も知りません。

両方の例外はバグを示しています。どちらも実稼働コードにスローされるべきではありません。問題は、どの例外がバグを持っているかをデバッグしている人に伝えるのかということです。null deref例外はほとんど何も教えてくれません。引数null例外は多くのことを伝えます。ユーザーに親切にしてください。彼らが彼らの間違いから学ぶことを可能にする例外を投げます。


"neither should ever be thrown in production code"他にどのような種類のコード/状況が使用されるのか理解できませんか?それらは次のようにコンパイルする必要がありDebug.Assertますか?または、APIからそれらをスローしないことを意味しますか?
StuperUser

6
@StuperUser:あなたは私が意味することを誤解したと思う、なぜなら私はそれを混乱した方法で書いたからだ。あなたは必要があります持ってthrow new ArgumentNullException本番コードで、そしてそれは、テストケースの外側を実行することはありません。呼び出し元がそのバグを持つことはないため、実際に例外がスローされることはありません。
エリックリッパー

1
誰がバグを持っているかを伝えるだけでなく、バ​​グの場所を伝えます。両方の領域が私の場合でも、どの変数がヌルであったかを正確に理解することは必ずしも容易ではありません。
オハドシュナイダー

5

ポイントは、あなたのコードが何か意味のあることをするべきだということです。

A NullReferenceExceptionはすべてを意味するため、特に意味がありません。例外がスローされた場所(およびコードが利用できない可能性があります)を確認せずに、何が悪かったのかわかりません。

An ArgumentNullExceptionは意味someObjectがありnullます。なぜなら、引数がであったために操作が失敗したからです。それを理解するためにあなたのコードを見る必要はありません。

また、この例外を発生させることは、あなたnullがそれを行う唯一の方法であるにもかかわらず、明示的に処理することを意味し、コードをクラッシュさせるだけで、実際にnullを処理できる可能性があることを意味する可能性があります、しかしケースを実装するのを忘れていました。

例外のポイントは、障害が発生した場合に情報を伝達することです。これは、実行時に処理して障害から回復するか、デバッグ時に情報を伝達して、問題をよりよく理解できます。


2

唯一の利点は、例外でより良い診断を提供できることです。つまり、関数の呼び出し元がnullを渡していますが、逆参照にそれをスローさせた場合、呼び出し元が間違ったデータを渡すか、関数自体にバグがあるかどうかはわかりません。

他の誰かが使いやすくするために関数を呼び出して(何かがうまくいかない場合にデバッグする)、ランタイムがとにかくそれをチェックするので、内部コードでそれをしないなら、私はそれをします。


1

ArgumentNullExceptionのスローは「正しい」と信じるように訓練されていますが、「オブジェクトのインスタンスに設定されていないオブジェクト参照」エラーは、バグがあることを意味します。

コードが設計どおりに機能しない場合、バグがあります。アンはNullReferenceExceptionシステムによってスローされ、その事実が本当に機能よりも多くのバグのためになります。処理する特定の例外を定義しなかったためだけではありません。また、コードを読んでいる開発者は、発生する状況を説明しなかったと想定する場合があるためです。

同様の理由で、ApplicationExceptionアプリケーションExceptionに属しているのと、オペレーティングシステムに属している一般的なものがあります。スローされる例外のタイプは、例外の発生場所を示します。

nullを考慮している場合は、特定の例外をスローすることで適切に処理するか、許容される入力である場合は、例外がスローされるため、例外をスローしないことをお勧めします。適切なデフォルトで操作を実行するか、操作をまったく行わない(たとえば、更新は不要)ことで処理します。

最後に、状況を処理する発信者の能力を無視しないでください。より具体的な例外を与えることにより、コンストラクター変数の初期化の失敗など、他のさまざまな理由で発生する可能性のあるArgumentExceptionより一般的な前にこのエラーを処理するようにtry / catchをNullReferenceException構築できます。


1

このチェックにより、何が起こっているかがより明確になりますが、実際の問題は次のようなコード(付随する)にあります。

public void DoSomething(Foo someOtherObject, ISomeInterface someObject)
{
    someOtherObject.DoThisOrThat(this, someObject);
}

ここにはコールチェーンが含まれており、nullチェックなしではsomeOtherObjectのエラーのみが表示され、問題が最初に明らかになった場所は表示されません。つまり、時間の無駄なデバッグまたはコールスタックの解釈を意味します。

一般に、この種のことと一貫性を保ち、常にnullチェックを追加する方が良いです。これにより、ケースごとに選択して選択するのではなく、欠落している場合に見つけやすくなります(nullがわかっていると仮定すると)常に無効)。


1

考慮すべきもう1つの理由は、nullオブジェクトが使用される前に何らかの処理が発生した場合はどうなるでしょうか

関連付けられた会社ID(Cid)と個人ID(Pid)のデータファイルがあるとします。番号のペアのストリームとして保存されます。

Cid Pid Cid Pid Cid Pid Cid Pid Cid Pid

そして、このVB関数はレコードをファイルに書き込みます。

Sub WriteRecord(Company As CompanyInfo, Person As PersonInfo)
    Using File As New StringWriter(DataFileName)
        File.Write(Company.ID)
        File.Write(Person.ID)
    End Using
End Sub

場合Personはnull参照である、ラインは、File.Write(Person.ID)例外がスローされます。ソフトウェアは例外をキャッチして回復できますが、これにより問題が残ります。会社IDは、関連する個人IDなしで記述されています。実際、次にこの関数を呼び出すときに、以前の個人IDが予期されていた場所に会社IDを配置します。そのレコードが正しくないことに加えて、後続のすべてのレコードには個人IDがあり、その後に会社IDが続きます。これは間違った方法です。

Cid Pid Cid Pid Cid Cid Pid Cid Pid Cid

関数の開始時にパラメーターを検証することにより、メソッドの失敗が保証されたときに処理が発生するのを防ぎます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.