web-dev-qa-db-ja.com

Debug.Assertと例外のスロー

私は、アサーションの使用方法とタイミングについて、多くの articles (およびStackOverflowに投稿されたその他のsimilarの質問)をたくさん読みました、そして私はそれらをよく理解した。しかし、それでも、私がDebug.Assert単純な例外をスローする代わりに。つまり、.NETでは、失敗したアサーションに対するデフォルトの応答は、「世界を停止」してユーザーにメッセージボックスを表示することです。この種の動作は変更できる可能性がありますが、それを行うのは非常に煩わしく冗長ですが、代わりに適切な例外をスローするだけで済みます。このようにして、例外をスローする直前にアプリケーションのログに簡単にエラーを書き込むことができ、さらに、アプリケーションが必ずしもフリーズするわけではありません。

だから、もし私が、もしそうなら、Debug.Assert単純な例外の代わりに?アサーションを本来あるべきでない場所に配置すると、あらゆる種類の「望ましくない動作」が発生する可能性があるため、私の見解では、例外をスローする代わりにアサーションを使用しても何も得られません。私に同意しますか、それともここで何か不足していますか?

注:「理論上」の違いは何なのか(デバッグとリリース、使用パターンなど)は完全に理解していますが、それを見ると、アサートを実行する代わりに、例外をスローすることをお勧めします。プロダクションリリースでバグが発見された場合でも、「アサーション」が失敗するようにしたいので(結局のところ、「オーバーヘッド」は途方もなく小さいので)、代わりに例外をスローしたほうがよいでしょう。


編集:アサートが失敗した場合の表示方法は、アプリケーションが何らかの破損した予期しない状態に入ったことを意味します。では、なぜ実行を続けたいのでしょうか?アプリケーションがデバッグバージョンとリリースバージョンのどちらで実行されているかは関係ありません。同じことが両方にあてはまる

91
unknown

あなたの推論がもっともらしいであることに同意します-つまり、アサーションが予期せず違反された場合、スローすることによって実行を停止することは理にかなっています-私は個人的にアサーションの代わりに例外を使用しません。理由は次のとおりです。

他の人が言ったように、アサーションは不可能であるdocumentシチュエーションである必要があります。不可能なとされる状況が発生した場合に、開発者に通知するような方法。対照的に、例外は、例外的な状況、起こりそうもない状況、または誤った状況に制御フローメカニズムを提供しますが、不可能ではありません。私にとって、主な違いはこれです:

  • 常に、指定されたthrowステートメントを実行するテストケースを作成できるようにする必要があります。このようなテストケースを作成することができない場合は、プログラムに実行されないコードパスがあり、デッドコードとして削除する必要があります。

  • アサーションを起動させるテストケースを生成することは決して不可能であるべきです。アサーションが発生した場合、コードが間違っているか、アサーションが間違っています。いずれにしても、コードで何かを変更する必要があります。

そのため、アサーションを例外に置き換えません。アサーションを実際に起動できない場合、例外で置き換えることは、プログラムにテスト不可能なコードパスがあることを意味します。テストできないコードパスが嫌いです。

171
Eric Lippert

アサーションは、プログラマーの世界に対する理解をチェックするために使用されます。アサーションは、プログラマが何か間違ったことをした場合にのみ失敗するはずです。たとえば、アサーションを使用してユーザー入力をチェックしないでください。

「起こり得ない」条件のテストをアサートします。例外は、「発生してはならないが発生する」条件です。

アサーションは、ビルド時(または実行時)にその動作を変更できるので便利です。たとえば、多くの場合、リリースビルドでは、アサーションは不要なオーバーヘッドが発生するため、チェックされません。これも注意すべき点です。テストが実行されない可能性もあります。

アサートの代わりに例外を使用すると、いくつかの値が失われます。

  1. アサートは1つだけですが、例外のテストとスローは少なくとも2行なので、コードはより冗長になります。

  2. テストとスローコードは常に実行されますが、アサートはコンパイルして取り除くことができます。

  3. アサートは、チェックしてスローする製品コードとは異なる意味を持つため、他の開発者との通信が一部失われます。実際にプログラミングアサーションをテストしている場合は、アサートを使用します。

詳細: http://nedbatchelder.com/text/assert.html

16
Ned Batchelder

EDIT:投稿で行った編集/メモへの応答:例外を使用することは正しいことのようですあなたが達成しようとしていることの種類のためにアサーションを使うよりも使いましょう。あなたがたどっている精神的な障害は、同じ目的を果たすために例外と主張を検討しているので、どちらが「正しい」のかを理解しようとしていることだと思います。アサーションと例外を使用する方法にはいくつかのオーバーラップがあるかもしれませんが、それらが同じ問題に対する異なるソリューションであることを混同しないでください-そうではありません。アサーションと例外にはそれぞれ独自の目的、長所、短所があります。

私は自分の言葉で答えをタイプするつもりでしたが、これは私が持っているよりもより良い概念をコンセプトにしています:

C#ステーション:アサーション

Assertステートメントの使用は、実行時にプログラムロジックエラーをキャッチする効果的な方法ですが、プロダクションコードから簡単に除外できます。開発が完了すると、コンパイル中にプリプロセッサシンボルNDEBUG [すべてのアサーションを無効にする]を定義するだけで、コーディングエラーに対するこれらの冗長テストの実行時コストを排除できます。ただし、アサート自体に配置されたコードは製品版では省略されることを忘れないでください。

アサーションは、以下のすべてが成立する場合にのみ条件をテストするために使用するのが最適です。

* the condition should never be false if the code is correct,
* the condition is not so trivial so as to obviously be always true, and
* the condition is in some sense internal to a body of software.

アサーションを使用して、ソフトウェアの通常の動作中に発生する状況を検出することはほとんどありません。たとえば、通常、アサーションはユーザーのエラーのチェックに使用しないでください。入力。ただし、アサーションを使用して、呼び出し元がユーザーの入力を既にチェックしたことを確認することは意味があります。

基本的に、本番アプリケーションでキャッチ/処理する必要があるものには例外を使用し、アサーションを使用して、開発には役立つが本番環境ではオフになっている論理チェックを実行します。

12
Tom Neyland

(考案された)実用的な例が違いを明らかにするのに役立つと思います:

MoreLinqのバッチ拡張)から変更

// 'public facing' method
public int DoSomething(List<string> stuff, object doohickey, int limit) {

    // validate user input and report problems externally with exceptions

    if(stuff == null) throw new ArgumentNullException("stuff");
    if(doohickey == null) throw new ArgumentNullException("doohickey");
    if(limit <= 0) throw new ArgumentOutOfRangeException("limit", limit, "Should be > 0");

    return DoSomethingImpl(stuff, doohickey, limit);
}

// 'developer only' method
private static int DoSomethingImpl(List<string> stuff, object doohickey, int limit) {

    // validate input that should only come from other programming methods
    // which we have control over (e.g. we already validated user input in
    // the calling method above), so anything using this method shouldn't
    // need to report problems externally, and compilation mode can remove
    // this "unnecessary" check from production

    Debug.Assert(stuff != null);
    Debug.Assert(doohickey != null);
    Debug.Assert(limit > 0);

    /* now do the actual work... */
}

Eric Lippert 他が言ったように、念のため、正しいと期待するものだけを表明しますあなた(開発者)が誤ってそれを間違ってどこかで使用したため、コードを修正できます。基本的に、何が発生するかを制御できない、または予測できない場合に例外をスローします。ユーザー入力のため、不正なデータを与えた場合でも適切に応答できます(ユーザーなど)。

7
drzaus

Code Complete の別のナゲット:

「アサーションは、仮定が真でない場合に大声で不平を言う関数またはマクロです。アサーションを使用して、コードで行われた仮定を文書化し、予期しない条件を洗い流します。..

「開発中、アサーションは矛盾する仮定、予期しない条件、ルーチンに渡された不正な値などを洗い流します。」

彼はさらに、何を主張すべきか、何を主張すべきでないかについていくつかのガイドラインを追加します。

一方、例外:

「例外処理を使用して、予期しないケースに注意を引きます。例外的なケースは、開発中に明白になり、本番用コードの実行中に回復できるように処理する必要があります。」

この本を持っていない場合は、購入する必要があります。

4

あなたがかなり大きなチームのメンバーであり、クラスでのオーバーラップを含め、同じ一般的なコードベースで作業している人が何人かいるとします。他のいくつかのメソッドによって呼び出されるメソッドを作成し、ロックの競合を回避するために、個別のロックを追加するのではなく、特定のロックを使用して呼び出しメソッドによって以前にロックされていたと想定します。など、Debug.Assert(RepositoryLock.IsReadLockHeld || RepositoryLock.IsWriteLockHeld);他の開発者は、呼び出し側メソッドがロックを使用する必要があるというコメントを見落とす可能性がありますが、これは無視できません。

0
daniel

[〜#〜] are [〜#〜]可能であるが発生してはならないものに対してアサーションを使用します(それが不可能な場合は、なぜアサーションを配置しますか?)。

Exceptionを使用するケースのように聞こえませんか? Exceptionの代わりにアサーションを使用するのはなぜですか?

なぜなら、アサーションのパラメーターがfalseになるのを止めるアサーションの前に呼び出されるコードがあるはずだからです。

通常、Exceptionの前には、スローされないことを保証するコードはありません。

Debug.Assert()がprodでコンパイルされるのはなぜ良いのですか?デバッグでそれについて知りたい場合は、製品でそれについて知りたくないですか?

開発中にのみ必要です。Debug.Assert(false)の状況を見つけたら、Debug.Assert(false)が再び発生しないことを保証するコードを記述します。開発が完了したら、Debug.Assert(false)シチュエーションを見つけて修正した場合、Debug.Assert()は冗長になるため、安全にコンパイルできます。

0
David Klempfner

Debug.Assertは、デフォルトではデバッグビルドでのみ機能するため、リリースビルドで予期しない不正な動作を検出するには、例外を使用するか、プロジェクトプロパティでデバッグ定数をオンにする必要があります(これは一般的には良い考えではありません)。

0
Mez