14

最後に、Stack Overflow で質問があります。:-)

主なターゲットは Java ですが、ほとんど言語にとらわれないと思います。ネイティブ アサートがない場合は、いつでもシミュレートできます。

私は Java で書かれた一連のソフトウェアを販売する会社で働いています。コードは古く、少なくとも Java 1.3 までさかのぼり、いくつかの場所でそれが示されています... これは大きなコード ベースであり、約 200 万行に及ぶため、一度にすべてをリファクタリングすることはできません。
最近、最新バージョンを Java 1.4 構文と JVM から Java 1.6 に切り替え、次のようないくつかの新機能を保守的に使用assertしました (以前は DEBUG.ASSERT マクロを使用していました -- assert1.4 で導入されたことは知っていますが、使用しませんでした)。以前)、ジェネリック (型指定されたコレクションのみ)、foreach ループ、列挙型など。

このトピックに関する記事をいくつか読んだことがありますが、私は assert の使用についてまだ少し緑色です。それでも、いくつかの使用法は私を困惑させ、私の常識を傷つけます... ^_^ それで、私が何かを修正したいのが正しいかどうか、またはそれが一般的な慣行に反するかどうかを確認するために、いくつか質問する必要があると思いました. 私は言葉が多いので、ざっと目を通すのが好きな人のために、質問を太字にしました。

参考までに、SO でassert javaを検索したところ、興味深いスレッドがいくつか見つかりましたが、正確な重複はないようです。

まず、今日私の質問を引き起こした主な問題:

SubDocument aSubDoc = documents.GetAt( i );
assert( aSubDoc != null );
if ( aSubDoc.GetType() == GIS_DOC )
{
   continue;
}
assert( aSubDoc.GetDoc() != null );
ContentsInfo ci = (ContentsInfo) aSubDoc.GetDoc();

(はい、私たちは MS の C/C++ スタイル/コード規則を使用しています。私もそれが好きです (同じバックグラウンドから来ました)! だから私たちを訴えてください。 )
まず、フォームは呼び出しassert()の変換に由来します。DEBUG.ASSERT()assert は言語構造であり、(もはやここでは) 関数呼び出しではないため、余分な括弧は嫌いです。私も嫌いですreturn (foo);:-)
次に、アサートはここで不変条件をテストしません。むしろ、悪い値に対するガードとして使用されます。しかし、私が理解しているように、ここでは役に立ちません。アサーションは、コンパニオン文字列で文書化されていなくても、アサーションが有効な場合にのみ例外をスローします。だから私たちが持っているなら-eaオプションを使用すると、通常の NullPointerException の代わりにアサーションがスローされるだけです。とにかく最高レベルで未チェックの例外をキャッチするため、これは最大の利点のようには見えません。
それらを取り除き、それで生活できると思いますか(つまり、Javaにそのような未確認の例外を発生させます)?(または、もちろん、他の場所で行われている可能性がある場合は、null 値に対してテストします)。

補足: 上記のスニペットで主張する必要がある場合は、ゲッターに対してではなく、ci 値に対して行います。ほとんどのゲッターが最適化/インライン化されている場合でも、確信が持てないため、2 回呼び出すことは避ける必要があります。

最後に参照されたスレッドで、パブリック メソッドはパラメーターの値に対してテストを使用する必要があり (パブリック API の使用)、プライベート メソッドは代わりにアサートに依存する必要があると誰かが言いました。いいアドバイス。
現在、どちらの種類のメソッドも、別のデータ ソースである外部入力をチェックする必要があります。すなわち。たとえば、ユーザー、データベース、ファイル、またはネットワークからのデータ。
私たちのコードでは、これらの値に対するアサートが見られます。私は常にこれらを実際のテストに変更するので、アサーションが無効になっていても機能します。これらは不変条件ではなく、適切に処理する必要があります。
たとえば、リレーションで使用される定数で満たされたデータベーステーブルなど、入力が定数であると想定される例外が1つだけあります。このテーブルが変更されても、対応するコードが更新されないと、プログラムが壊れます。
他の例外はありますか?

私が見た別の比較的頻繁な使用は、問題ないようです: スイッチのデフォルトで、または else ifすべての可能な値をテストする一連の最後で (これらのケースは、列挙型を使用する前にさかのぼります!)、多くの場合、assert false : "Unexpected value for stuff: " + stuff;
正規の Lookがあります。私(これらのケースは本番環境では発生しないはずです)、どう思いますか?(ここでは関係のない「スイッチなし、OOを使用する」というアドバイスを超えて)。

最後に、ここで見逃した便利なユースケースや厄介な落とし穴は他にありますか? (おそらく!)

4

4 に答える 4

10

一番のルールは、アサーションの副作用を避けることです。つまり、コードは、アサーションがオンになっていて失敗しない場合と同じように、アサーションがオフになっている場合と同じように動作する必要があります (明らかに、失敗したアサーションはエラーを発生させるため、動作を変更します)。

第 2 のルールは、重要なチェックにアサーションを使用しないことです。それらはオフにすることができます (より正確には、オンにしないでください)。非プライベート メソッドのパラメーター チェックには、IllegalArgumentException を使用します。

アサーションは実行可能な仮定です。私はアサーションを使用して、プログラムの現在の状態に関する私の信念を述べています。たとえば、「ここでは n が正であると仮定します」「リストにはここで正確に 1 つの要素があると仮定します」などです。

于 2008-12-16T00:20:40.927 に答える
3

assertパラメータの検証だけでなく、スレッドの検証にも使用します。私はswingを行うたびに、ほとんどすべてのメソッドにassertを書いて、「ワーカースレッド/AWTThreadでのみ実行する必要があります」とマークしています。(Sun が代わりにやってくれると思います。) Swing スレッド モデルのため、非 UI スレッドから Swing API にアクセスしても失敗しない (そしてランダムに失敗する) 場合があります。アサートなしでこれらすべての問題を見つけることは非常に困難です。

私が想像できるもう 1 つの例は、JAR に含まれるリソースを確認することです。NPE ではなく、英語の例外を設定できます。


編集: 別の例; オブジェクトロックチェック。ネストされた同期ブロックを使用することがわかっている場合、またはデッドロックを修正する場合は、Thread.holdLock(Object)を使用して、逆の順序でロックを取得しないようにします。


EDIT(2): 絶対に到達してはならないコード ブロックがあると確信している場合は、次のように記述できます。

throw new AssertionError("You dead");

むしろその後

assert false:"I am lucky";

たとえば、変更可能なオブジェクトで "equals(Object)" をオーバーライドする場合、それがキーになることがないと思われる場合は、hashCode() を AssertionError でオーバーライドします。この方法は、一部の書籍で推奨されています。パフォーマンスを損なうことはありません (決して到達してはならないため)。

于 2008-12-15T16:16:50.043 に答える
2

一般的にアサーションを避けるべきだと私が考える理由の多くに触れました。アサートの使用に非常に厳密なガイドラインがあるコードベースで作業していない限り、アサーションをオフにできない状況にすぐに陥ります。その場合、通常のロジック テストを使用するだけでよいでしょう。

したがって、私の推奨事項は、アサーションをスキップすることです。言語が代わりに行う余分な null ポインター チェックに固執しないでください。ただし、ポインターがしばらく逆参照されない可能性がある場合は、事前に null チェックを行うことをお勧めします。また、「絶対に」起こらないケース (最後の if 分岐またはデフォルトの switch ケース) には常に実際の例外を使用し、「assert false」は使用しないでください。アサーションを使用すると、誰かがそれをオフにする可能性があり、状況が実際に発生した場合、事態は非常に混乱します。

于 2008-12-15T15:58:47.413 に答える
2

パブリック (API) メソッドのパラメーターをチェックし、パラメーターが有効でない場合は IllegalArgumentException をスローすることをお勧めします。API ユーザーが適切なエラー (メッセージ) を取得する必要があるため、ここではアサートしません。

アサートは、事後条件および場合によっては事前条件をチェックするために、非パブリック メソッドで使用する必要があります。例えば:

List returnListOfSize(int size) {
    // complex list creation
    assert list.size == size;
}

多くの場合、巧妙なエラー処理戦略を使用すると、アサートを回避できます。

于 2008-12-15T15:59:02.840 に答える