33

John Robbins による「Debugging MS .Net 2.0 Applications」をざっと読み始めたところですが、彼の Debug.Assert(...) の伝道に混乱しています。

彼は、適切に実装された Assert は、エラー状態の状態をある程度格納することを指摘しています。たとえば、次のようになります。

Debug.Assert(i > 3, "i > 3", "This means I got a bad parameter");

さて、個人的には、彼が実際の賢明な「ビジネス ロジック」のコメントなしでテストを言い直すのが大好きなのがおかしいように思えます。

だから、私はAssertsを一種の低レベルの「私の仮定を守ろう」のようなものとして得ていると思います...これがデバッグでのみ行う必要があるテストであると感じていると仮定します-つまり、同僚から自分自身を保護していますそして将来のプログラマー、そして彼らが実際に物事をテストすることを望んでいます。

しかし、私が理解できないのは、彼は続けて、通常のエラー処理に加えてアサーションを使用する必要があると言っているということです。今、私が想定しているのは次のようなものです:

Debug.Assert(i > 3, "i must be greater than 3 because of the flibbity widgit status");
if (i <= 3)
{
    throw new ArgumentOutOfRangeException("i", "i must be > 3 because... i=" + i.ToString());
}

エラー条件テストの Debug.Assert の繰り返しによって何が得られましたか? 非常に重要な計算のデバッグのみのダブルチェックについて話しているなら、私はそれを理解できると思います...

double interestAmount = loan.GetInterest();
Debug.Assert(debugInterestDoubleCheck(loan) == interestAmount, "Mismatch on interest calc");

...しかし、(DEBUGビルドとリリースビルドの両方で)確かにチェックする価値があるパラメーターテストでは取得できません...またはそうではありません。私は何が欠けていますか?

4

8 に答える 8

50

アサーションはパラメータ チェック用ではありません。パラメーターのチェックは常に (ドキュメントや仕様で指定されている前提条件に従って) 実行し、必要に応じてArgumentOutOfRangeExceptionスローする必要があります。

アサーションは、「あり得ない」状況、つまり (プログラム ロジックで)真であると仮定するものをテストするためのものです。アサーションは、これらの仮定が何らかの理由で破られているかどうかを示すためにあります。

お役に立てれば!

于 2008-09-14T09:26:56.133 に答える
17

アサートと例外スローには通信の側面があります。

Name プロパティと ToString メソッドを持つ User クラスがあるとします。

ToString が次のように実装されている場合:

public string ToString()
{
     Debug.Assert(Name != null);
     return Name;
}

Name は null であってはならず、そうである場合は User クラスにバグがあると書かれています。

ToString が次のように実装されている場合:

public string ToString()
{
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

Name が null の場合、呼び出し元が ToString を正しく使用していないため、呼び出す前に確認する必要があることを示しています。

両方を使用した実装

public string ToString()
{
     Debug.Assert(Name != null);
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

Name が null の場合、User クラスにバグがありますが、とにかくそれを処理したいと言っています。(ユーザーは、電話をかける前に名前を確認する必要はありません。) これが Robbins 氏が推奨していた安全策だと思います。

于 2008-09-14T10:57:27.680 に答える
7

テストに関する懸念事項に関して、デバッグとアサートのガイダンスを提供することになると、私はこれについて長く懸命に考えてきました。

誤った入力、悪い状態、無効な操作順序、およびその他の考えられるエラー状態でクラスをテストできる必要があり、アサートは決してトリップしないはずです。各アサートは、実行された入力や計算に関係なく、 何かが常に真であることを確認しています。

私がたどり着いた良い経験則:

  1. アサートは、構成に関係なく正しく機能する堅牢なコードに代わるものではありません。それらは補完的です。

  2. 単体テストの実行中は、無効な値を入力したり、エラー条件をテストしたりする場合でも、アサートをトリップしないでください。コードは、アサートを発生させずにこれらの条件を処理する必要があります。

  3. アサートがトリップした場合 (単体テストまたはテスト中に)、クラスにバグがあります。

他のすべてのエラー (通常は環境 (ネットワーク接続が失われた) または誤用 (呼び出し元が null 値を渡した) によるもの) については、ハード チェックと例外を使用する方がはるかに適切で理解しやすいものです。例外が発生した場合、呼び出し元はそれが自分のせいである可能性が高いことを認識しています。アサートが発生した場合、呼び出し元は、アサートが配置されているコードのバグである可能性が高いことを認識しています。

重複について:同意します。Debug.Assert と例外チェックを使用して検証を複製する理由がわかりません。コードに多少のノイズが追加され、誰に問題があるかがわかりにくくなるだけでなく、一種の繰り返しになります。

于 2010-01-09T16:06:10.720 に答える
4

パブリックメソッドとプロテクトメソッドで例外をスローし、プライベート メソッドでアサーションをスローする明示的なチェックを使用します。

通常、明示的なチェックにより、プライベート メソッドが誤った値を認識しないように保護されます。本当に、アサートは不可能な状態をチェックしています。アサートが発生した場合は、クラスのパブリック ルーチンの 1 つに含まれる検証ロジックに欠陥があることを示しています。

于 2008-09-14T09:55:08.080 に答える
3

例外をキャッチして飲み込み、エラーをテストから見えなくすることができます。これは Debug.Assert では起こりません。

すべての例外をキャッチする catch ハンドラーを持つべきではありませんが、それでも人々はそれを行い、時には避けられないこともあります。コードが COM から呼び出された場合、相互運用層はすべての例外をキャッチし、それらを COM エラー コードに変換します。つまり、未処理の例外は表示されません。アサートはこれに悩まされません。

また、例外が処理されない場合は、ミニダンプを取得することをお勧めします。VB が C# よりも強力な領域の 1 つは、例外フィルターを使用して、例外が処理中のときにミニダンプをスナップし、残りの例外処理を変更しないままにしておくことができることです。 例外フィルター注入に関する Gregg Miskelly のブログ投稿では、c# からこれを行う便利な方法が提供されています。

アセットに関するもう 1 つの注意事項 ... コード内のエラー条件の単体テストとの相互作用が不十分です。単体テストのアサートをオフにするラッパーを用意することは価値があります。

于 2008-09-14T21:59:11.350 に答える
2

IMO 開発時間の損失のみです。適切に実装された例外により、何が起こったかを明確に把握できます。「Assertion failed: i < 10」というあいまいなエラーを示すアプリケーションが多すぎました。アサーションは一時的な解決策だと思います。私の意見では、アサーションはプログラムの最終バージョンにあるべきではありません。私の実践では、アサーションを使用して迅速で汚いチェックを行いました。コードの最終バージョンは、誤った状況を考慮に入れ、それに応じて動作する必要があります。何か悪いことが起こった場合、対処するか放置するかの 2 つの選択肢があります。間違ったパラメーターが渡された場合、関数は意味のある説明で例​​外をスローする必要があります。検証ロジックの重複にポイントはありません。

于 2008-09-14T09:25:40.647 に答える
1

Assert の適切な使用例:

Debug.Assert(flibbles.count() < 1000000, "too many flibbles"); // indicate something is awry
log.warning("flibble count reached " + flibbles.count()); // log in production as early warning

個人的には、何かが望ましい制限を超えていることがわかっている場合にのみAssert を使用する必要があると思いますが、続行しても合理的に安全であると確信できます。他のすべての状況 (私が思いもよらなかった状況を自由に指摘してください) では、例外を使用して、ハードかつ迅速に失敗します。

私にとっての重要なトレードオフは、破損を回避してトラブルシューティングを容易にするために、ライブ/本番システムを例外でダウンさせたいかどうか、またはテスト/デバッグ バージョンで見過ごされてはならない状況に遭遇したことがあるかどうかです。本番環境での継続が許可されます (もちろん警告をログに記録します)。

参照。http://c2.com/cgi/wiki?Java の質問からコピーおよび変更された FailFast: Exception Vs Assertion

于 2011-05-11T12:31:25.817 に答える
0

こちらは2セントです。

アサーションと例外の両方を使用するのが最善の方法だと思います。2 つのメソッドの主な違いは、その Assert ステートメントをアプリケーション テキスト (定義、条件付き属性など) から簡単に削除できる場合、imho であり、スローされる例外は (典型的には) 削除するのが難しい条件付きコードに依存します (プリプロセッサ条件付きの複数セクション)。

すべてのアプリケーション例外は正しく処理されますが、アサーションはアルゴリズムの開発とテスト中にのみ満たされる必要があります。

null オブジェクト参照をルーチン パラメーターとして渡し、この値を使用すると、null ポインター例外が発生します。確かに: なぜアサーションを書く必要があるのですか? この場合時間の無駄です。しかし、クラス ルーチンで使用されるプライベート クラス メンバーはどうでしょうか。これらの値がどこかに設定されている場合は、null 値が設定されているかどうかをアサーションで確認することをお勧めします。これは、メンバーを使用すると null ポインター例外が発生するが、値がどのように設定されたかがわからないためです。これにより、プライベート メンバーを設定するために使用されるすべてのエントリ ポイントで中断するプログラムが再起動されます。

例外はより便利ですが、(imho) 管理が非常に重くなる可能性があり、例外を使いすぎる可能性があります。また、追加のチェックが必要であり、コードの最適化には望ましくない可能性があります。個人的には、コードで深い catch コントロールが必要な場合 (catch ステートメントはコール スタックの非常に低い位置)、または関数パラメーターがコードでハードコーディングされていない場合にのみ、例外を使用します。

于 2010-01-09T15:22:56.597 に答える