241

私は、CSの学位を取得して卒業し、約1年間プロのソフトウェアエンジニアを務めています。私はC++とCでアサーションについてしばらく知っていましたが、最近までC#と.NETにアサーションが存在することをまったく知りませんでした。

私たちの製品コードにはアサートがまったく含まれていません。私の質問はこれです...

プロダクションコードでAssertsの使用を開始する必要がありますか?もしそうなら、その使用はいつ最も適切ですか?するほうが理にかなっていますか

Debug.Assert(val != null);

また

if ( val == null )
    throw new exception();
4

20 に答える 20

256

John Robbins は、 Microsoft .NET 2.0 アプリケーションのデバッグで、アサーションに関する大きなセクションを持っています。彼の主なポイントは次のとおりです。

  1. 自由に主張する。アサーションが多すぎることはありません。
  2. アサーションは例外を置き換えません。例外は、コードが要求するものをカバーします。アサーションは、それが想定するものをカバーします。
  3. 適切に記述されたアサーションは、何がどこで (例外のように) 発生したかだけでなく、その理由も教えてくれます。
  4. 多くの場合、例外メッセージは不可解であり、コードをさかのぼってエラーの原因となったコンテキストを再作成する必要があります。アサーションは、エラーが発生した時点でのプログラムの状態を保持できます。
  5. アサーションはドキュメントとしても機能し、コードが依存している暗黙の前提を他の開発者に伝えます。
  6. アサーションが失敗したときに表示されるダイアログでは、デバッガーをプロセスにアタッチできるため、そこにブレークポイントを置いたかのようにスタックを探索できます。

PS: Code Complete が気に入った場合は、この本でフォローアップすることをお勧めします。WinDBGやダンプファイルの使い方を知りたくて購入したのですが、前半はそもそもバグを回避するためのヒントが満載です。

于 2008-09-24T19:47:21.260 に答える
93

コードDebug.Assert()内のどこにでも配置して、不変条件を確認するための健全性チェックを行います。リリースビルドをコンパイルすると(つまり、DEBUGコンパイラ定数がない場合)、への呼び出しはDebug.Assert()削除されるため、パフォーマンスに影響を与えることはありません。

を呼び出す前に、例外をスローする必要がありますDebug.Assert()。アサーションは、開発中のすべてが期待どおりであることを確認するだけです。

于 2008-09-24T18:59:41.780 に答える
53

FWIW ...私のパブリックメソッドはif () { throw; }、メソッドが正しく呼び出されるようにパターンを使用する傾向があることがわかりました。私のプライベート メソッドは、 を使用する傾向がありますDebug.Assert()

アイデアは、私のプライベート メソッドでは、私が制御下にあるということです。そのため、間違ったパラメーターを使用して自分のプライベート メソッドの 1 つを呼び出し始めた場合、どこかで自分の仮定を破ったことになります。その状態に。本番環境では、これらのプライベート アサーションは理想的には不要な作業である必要があります。これは、内部状態を有効かつ一貫性のある状態に保つ必要があるためです。実行時に誰でも呼び出すことができる public メソッドに指定されたパラメーターとは対照的です。例外をスローして、パラメーターの制約を適用する必要があります。

さらに、実行時に何かが機能しない場合 (ネットワーク エラー、データ アクセス エラー、サード パーティ サービスから取得した不正なデータなど) に、プライベート メソッドが例外をスローする可能性があります。私のアサートは、オブジェクトの状態に関する私自身の内部の仮定を破っていないことを確認するためのものです。

于 2011-02-17T15:48:58.867 に答える
52

コードコンプリートから

8 防御的プログラミング

8.2 アサーション

アサーションは、開発中に使用されるコード (通常はルーチンまたはマクロ) であり、プログラムが実行時に自身をチェックできるようにします。アサーションが true の場合、すべてが期待どおりに動作していることを意味します。false の場合は、コードで予期しないエラーが検出されたことを意味します。たとえば、顧客情報ファイルのレコード数が 50,000 を超えることはないとシステムが想定している場合、プログラムには、レコード数が 50,000 以下であるというアサーションが含まれている可能性があります。レコード数が 50,000 以下である限り、アサーションはサイレントになります。ただし、50,000 を超えるレコードに遭遇すると、プログラムにエラーがあることを大声で「主張」します。

アサーションは、大規模で複雑なプログラムや信頼性の高いプログラムで特に役立ちます。これにより、プログラマーは、インターフェイスの前提条件の不一致や、コードの変更時に忍び寄るエラーなどをより迅速に洗い流すことができます。

通常、アサーションは 2 つの引数を取ります。真であると想定される仮定を記述するブール式と、そうでない場合に表示するメッセージです。

(…)

通常、実稼働コードでアサーション メッセージをユーザーに表示することは望ましくありません。アサーションは、主に開発および保守中に使用するためのものです。アサーションは通常、開発時にコードにコンパイルされ、本番用にコードからコンパイルされます。開発中、アサーションは、矛盾する仮定、予期しない条件、ルーチンに渡された不正な値などを洗い流します。運用中は、アサーションがシステム パフォーマンスを低下させないように、コードからコンパイルされます。

于 2008-09-24T19:12:30.750 に答える
45

アサートを使用して開発者の仮定をチェックし、例外を使用して環境の仮定をチェックします。

于 2008-09-24T19:01:33.570 に答える
32

私があなたなら、私はするだろう:

Debug.Assert(val != null);
if ( val == null )
    throw new exception();

または、条件チェックの繰り返しを避けるため

if ( val == null )
{
    Debug.Assert(false,"breakpoint if val== null");
    throw new exception();
}
于 2008-09-24T19:06:36.630 に答える
25

本番コード(つまり、リリースビルド)でAssertが必要な場合は、Debug.Assertの代わりにTrace.Assertを使用できます。

もちろん、これにより本番実行可能ファイルにオーバーヘッドが追加されます。

また、アプリケーションがユーザーインターフェイスモードで実行されている場合、デフォルトで[アサーション]ダイアログが表示されます。これは、ユーザーにとって少し戸惑うかもしれません。

DefaultTraceListenerを削除することで、この動作をオーバーライドできます。MSDNのTrace.Listenersのドキュメントを参照してください。

要約すれば、

  • Debug.Assertを自由に使用して、デバッグビルドのバグをキャッチします。

  • ユーザーインターフェイスモードでTrace.Assertを使用する場合は、ユーザーの混乱を避けるために、DefaultTraceListenerを削除することをお勧めします。

  • テストしている条件がアプリで処理できないものである場合は、実行が続行されないように、例外をスローすることをお勧めします。ユーザーはアサーションを無視することを選択できることに注意してください。

于 2008-09-24T19:02:10.777 に答える
23

アサートは、ユーザー エラーではなく、プログラマー (ユーザー) のエラーをキャッチするために使用されます。これらは、ユーザーがアサートを発生させる可能性がない場合にのみ使用する必要があります。たとえば、API を作成している場合、API ユーザーが呼び出すことができるメソッドで引数が null でないことを確認するためにアサートを使用しないでください。ただし、API の一部として公開されていないプライベート メソッドで使用して、想定されていないときにコードが null 引数を渡さないことを主張できます。

よくわからないときは、通常、アサートよりも例外を好みます。

于 2008-09-24T19:04:35.357 に答える
10

私の本にはほとんどありません。ほとんどの場合、すべてが正常であるかどうかを確認したい場合は、そうでない場合はスローします。

私が嫌いなのは、デバッグビルドがリリースビルドと機能的に異なるという事実です。デバッグアサーションが失敗しても機能がリリースで機能する場合、それはどのように意味がありますか?アサーターが長い間会社を辞め、コードのその部分を誰も知らない場合は、さらに良いでしょう。次に、問題を調査して、それが本当に問題であるかどうかを確認するために、時間を割く必要があります。それが問題であるなら、なぜその人はそもそも投げないのですか?

私にとって、これは、問題を他の誰かに延期しているDebug.Assertsを使用して、自分で問題に対処することを示唆しています。何かが当てはまるはずで、そうでない場合はスローします。

アサートを最適化する必要があり、そこで役立つパフォーマンスクリティカルなシナリオが存在する可能性があると思いますが、そのようなシナリオにはまだ遭遇していません。

于 2008-09-24T19:27:26.393 に答える
7

IDesign Standardによると、

すべての仮定を主張します。平均して、5 行ごとにアサーションです。

using System.Diagnostics;

object GetObject()
{...}

object someObject = GetObject();
Debug.Assert(someObject != null);

免責事項として、私はこの IRL を実装することが実用的であるとは思わなかったことに言及しておく必要があります。しかし、これは彼らの標準です。

于 2008-09-24T20:04:43.263 に答える
6

すべてのアサートは、次のように最適化できるコードである必要があります。

Debug.Assert(true);

すでに想定していることが真実であることを確認しているからです。例えば:

public static void ConsumeEnumeration<T>(this IEnumerable<T> source)
{
  if(source != null)
    using(var en = source.GetEnumerator())
      RunThroughEnumerator(en);
}
public static T GetFirstAndConsume<T>(this IEnumerable<T> source)
{
  if(source == null)
    throw new ArgumentNullException("source");
  using(var en = source.GetEnumerator())
  {
    if(!en.MoveNext())
      throw new InvalidOperationException("Empty sequence");
    T ret = en.Current;
    RunThroughEnumerator(en);
    return ret;
  }
}
private static void RunThroughEnumerator<T>(IEnumerator<T> en)
{
  Debug.Assert(en != null);
  while(en.MoveNext());
}

上記では、null パラメーターに対して 3 つの異なるアプローチがあります。最初のものはそれを許容できるものとして受け入れます (何もしません)。2 番目は、呼び出し元のコードが処理する例外をスローします (または、エラー メッセージが表示されます)。3番目は、それが起こりえないと仮定し、そうであると主張します。

最初の場合は問題ありません。

2 番目のケースでは、呼び出しコードに問題があります。null で呼び出すべきではなかっGetFirstAndConsumeたため、例外が返されます。

3 番目のケースでは、このコードに問題があります。これは、呼び出される前に既にチェックされているはずen != nullなので、正しくないのはバグです。言い換えると、理論的には に最適化できるコードであるべきでありDebug.Assert(true)、sicneen != nullは常にtrue!

于 2013-12-29T22:43:55.950 に答える
6

リリース ビルドのチェックを削除する場合にのみ、アサーションを使用してください。デバッグ モードでコンパイルしないと、アサーションが起動しないことに注意してください。

null のチェックの例を考えると、これが内部専用 API にある場合は、アサーションを使用する可能性があります。パブリック API にある場合は、明示的なチェック アンド スローを確実に使用します。

于 2008-09-24T19:03:56.823 に答える
4

Debug.Assert が適切な選択である可能性がある場合、さらに 4 つのケースを追加すると考えました。

1)ここで言及されていないのは、Asserts が自動化されたテスト中に提供できる追加の概念的なカバレッジです。簡単な例として:

より高いレベルの呼び出し元が、追加のシナリオを処理するためにコードの範囲を拡張したと考えている作成者によって変更された場合、理想的には (!) この新しい条件をカバーする単体テストを作成します。その場合、完全に統合されたコードが正常に動作しているように見える場合があります。

ただし、実際には微妙な欠陥が導入されていますが、テスト結果では検出されていません。この場合、呼び出し先は非決定論的になり、たまたま期待される結果しか提供しません。または、見過ごされていた丸め誤差が発生した可能性があります。または、他の場所で均等にオフセットされたエラーが発生しました。または、要求されたアクセス権だけでなく、付与されるべきではない追加の権限が付与されます。等。

この時点で、呼び出し先に含まれる Debug.Assert() ステートメントは、単体テストによって駆動される新しいケース (またはエッジ ケース) と組み合わされて、テスト中に元の作成者の仮定が無効になり、コードが無効になってはならないという非常に貴重な通知を提供できます。追加審査なしでリリースされます。単体テストを伴うアサートは完璧なパートナーです。

2)さらに、一部のテストは簡単に作成できますが、最初の仮定を考えると、コストが高く不要です。例えば:

特定の保護されたエントリ ポイントからのみオブジェクトにアクセスできる場合、すべてのオブジェクト メソッドからネットワーク権限データベースに対して追加のクエリを実行して、呼び出し元にアクセス許可があることを確認する必要がありますか? 確かにそうではありません。おそらく理想的なソリューションには、キャッシングまたはその他の機能拡張が含まれますが、設計上は必要ありません。Debug.Assert() は、オブジェクトが安全でないエントリ ポイントにアタッチされるとすぐに表示されます。

3)次に、場合によっては、製品がリリース モードで展開されたときに、その操作のすべてまたは一部に対して有用な診断インタラクションがない場合があります。例えば:

組み込みのリアルタイム デバイスであるとします。不正なパケットに遭遇したときに例外をスローして再起動すると、逆効果になります。代わりに、デバイスは、出力にノイズをレンダリングする点でも、ベストエフォート動作の恩恵を受ける可能性があります。また、ヒューマン インターフェイスやロギング デバイスがなく、リリース モードでデプロイされた場合、人間が物理的にアクセスできない場合もあります。エラーの認識は、同じ出力を評価することによって提供されるのが最適です。この場合、リベラルなアサーションと完全なプレリリース テストは、例外よりも価値があります。

4)最後に、呼び出し先が非常に信頼できると認識されているという理由だけで、一部のテストは不要です。ほとんどの場合、コードが再利用可能であるほど、信頼性を高めるために多くの努力が払われています。したがって、呼び出し元からの予期しないパラメーターに対しては Exception に、呼び出し先からの予期しない結果に対しては Assert するのが一般的です。例えば:

コアString.Find操作で-1、検索基準が見つからない場合に a が返されると記述されている場合、3 つではなく 1 つの操作を安全に実行できる可能性があります。ただし、実際に が返さ-2れた場合は、適切な対応策がない可能性があります。より単純な計算を、値を個別にテストする計算に置き換えるのは役に立たず-1、ほとんどのリリース環境では、コア ライブラリが期待どおりに動作することを確認するテストをコードに散らかすことは不合理です。この場合、アサートは理想的です。

于 2013-12-21T04:55:12.267 に答える
4

The Pragmatic Programmerからの引用: From Journeyman to Master

アサーションをオンのままにする

コンパイラーや言語環境を作成する人々によって広められた、アサーションに関する一般的な誤解があります。次のようになります。

アサーションは、コードにいくらかのオーバーヘッドを追加します。発生してはならないことをチェックするため、コードのバグによってのみトリガーされます。コードがテストされて出荷されると、それらは不要になり、コードをより高速に実行できるようにオフにする必要があります。アサーションはデバッグ機能です。

ここには明らかに間違った仮定が 2 つあります。まず、テストによってすべてのバグが見つかると想定しています。実際には、どんな複雑なプログラムでも、コードが通過する順列のほんのわずかなパーセンテージでさえもテストすることはほとんどありません (無慈悲なテストを参照してください)。

第二に、楽観主義者は、プログラムが危険な世界で実行されることを忘れています。テスト中、ネズミが通信ケーブルをかじったり、ゲームをプレイしてもメモリが使い果たされたり、ログ ファイルでハード ドライブがいっぱいになることはありません。これらのことは、プログラムが実稼働環境で実行されるときに発生する可能性があります。防御の最初のラインは考えられるエラーをチェックすることであり、2 つ目はアサーションを使用して見逃したエラーを検出しようとすることです。

プログラムを本番環境に配布するときにアサーションをオフにすることは、実際に一度渡ったことがあるため、ネットなしで高いワイヤーを渡るようなものです。劇的な価値はありますが、生命保険に加入するのは難しいです。

パフォーマンスの問題が発生した場合でも、本当に影響を受けるアサーションのみをオフにします

于 2015-11-18T16:04:30.763 に答える
2

常に 2 番目のアプローチ (例外のスロー) を使用する必要があります。

また、本番環境 (およびリリース ビルドがある) の場合は、無効な値を処理して顧客のデータを破壊するよりも、例外をスローする (最悪の場合はアプリをクラッシュさせる) 方が良いでしょう (これには数千ドルの費用がかかる可能性があります)。ドルの)。

于 2008-09-24T19:09:30.603 に答える
0

プログラムの論理エラーをテストするには、Debug.Assert を使用する必要があります。コンパイラは、構文エラーのみを通知できます。したがって、Assert ステートメントを使用して論理エラーをテストする必要があります。たとえば、青色の BMW だけが 15% 割引されるように車を販売するプログラムをテストするとします。コンパイラは、これを実行する際にプログラムが論理的に正しいかどうかについて何も教えてくれませんが、assert ステートメントはそうすることができます。

于 2008-09-24T19:03:40.913 に答える
-1

C#と.NETでどのように動作するかはわかりませんが、Cでは-DDEBUGを使用してコンパイルした場合にのみassert()が機能します。エンドユーザーは、なしでコンパイルした場合、assert()を表示しません。開発者専用です。私はそれを本当に頻繁に使用します、バグを追跡する方が簡単な場合があります。

于 2008-09-24T19:00:12.497 に答える
-2

私はそれらを本番コードでは使用しません。例外をスローし、キャッチしてログに記録します。

また、asp.netではアサーションがコンソールに表示されてリクエストがフリーズする可能性があるため、注意する必要があります。

于 2008-09-24T19:00:58.703 に答える