お気に入りの(賢い)防御的プログラミングのベストプラクティス[終了]


148

防御コーディングにお気に入りの(賢い)テクニックを選択する必要がある場合、それらはどのようなものでしょうか?私の現在の言語はJavaおよびObjective-C(背景はC ++)ですが、どの言語でもお気軽に回答してください。ここでの重点は、70%以上の私たちがすでに知っている以外の巧妙な防御技術にあります。さあ、今こそあなたのトリックの袋を深く掘り下げる時です。

つまり、この面白くない例以外を考えてみてください。

  • if(5 == x) の代わりに if(x == 5):意図しない割り当てを回避する

ここにいくつかの興味深い興味深い防御的プログラミングの例があります(言語固有の例はJavaにあります):

-変数を変更する必要があることがわかるまで、変数をロックします。

つまり、変数を変更する必要があることがわかるまで、すべての変数を宣言finalできますfinal。その時点で、を削除できます。一般的に知られていない事実の1つは、これがメソッドparamsにも有効であることです。

public void foo(final int arg) { /* Stuff Here */ }

-何か悪いことが起こった場合、証拠の痕跡を残してください

例外がある場合にできることはたくさんあります。明らかにそれをログに記録し、いくつかのクリーンアップを実行することは少数です。しかし、証拠の痕跡を残すこともできます(たとえば、「UNABLE TO LOAD FILE」や99999などの変数をセンチネル値に設定すると、例外catchブロックを突破した場合にデバッガーで役立ちます)。

-一貫性に関しては、悪魔は細部にあります

使用している他のライブラリと同じようにしてください。たとえば、Javaで、値の範囲を抽出するメソッドを作成している場合は、下限を包含し、上限を排他にします。これはString.substring(start, end)、同じように動作するようなメソッドとの一貫性を保ちます。それはインデックスはゼロ(からされている配列と一致要素の反復など、さまざまな操作になりように、このように動作するには、Sun JDKのメソッドのこれらのタイプのすべてを見つけることができます包括的な配列(の長さ)、排他的に)。

それで、あなたの好きな防御行動は何ですか?

更新:まだ行っていない場合は、遠慮なくお知らせください。公式回答を選択する前に、さらに多くの回答が寄せられる可能性があります。


ここでは、簡単なテクニックを知らないかもしれない無知な私たちの30%を表現したいと思います。誰もが基礎として誰もが知っておくべき「明白な」テクニックへのリンクがありますか?
elliot42 2009年


なぜあなたは公式の答えを選ぶのですか?それはまったく賢くないようです。
bzlm 2009年

まあ、プログラミングに関して言えば、賢い人でさえ、「驚きの最小の法則」に敬意を表して後席を取るべきです。私がスタックオーバーフローの精神を破ってベストアンサーをマークすることは、スタックオーバーフロープロトコルに違反することになります(そのため、上記のルールに違反します)。それとは別に、私は閉鎖が好きです:-)
Ryan Delucchi 2009年

回答:


103

c ++では、fence-postエラーをキャッチするための追加のメモリを提供するために、newを再定義するのが好きでした。

現在、私は、テスト駆動開発を支持して、防御的プログラミングを避けることを好みます。エラーをすばやく外部的にキャッチする場合は、防御策を講じてコードをだらだらとする必要はありません。コードはDRYであり、防御しなければならないエラーが少なくて済みます。

WikiKnowledge Wroteとして

防御的プログラミングを避け、代わりに速く失敗する。

防御的プログラミングとは、データの一部の障害を補おうとするコードを書く習慣、つまり、呼び出し元とサブルーチンの間の規約に準拠しないデータを呼び出し元が提供する可能性があり、サブルーチンが何らかの形で対処する必要があると想定するコードを書く習慣を意味しますそれと。


8
防御的プログラミングは、プログラムの他の部分によってもたらされる違法な条件に対処しようとしています。不適切なユーザー入力の処理は、まったく異なります。
Joe Soul-bringer

5
エラー...防御的プログラミングのこの定義は、質問で暗黙的に使用されている定義にさえ近づいていないことに注意してください。
ソル、

15
防御的プログラミングを避けてはいけません。このことは、データの障害を「補償」するのではなく、コードに本来あるべきでないことをさせるように設計された悪意のあるデータから身を守ります。バッファオーバーフロー、SQLインジェクションを参照してください。XSSの下でWebページほど速く失敗するものはありませんが、それはかなりではありません
ホルヘコルドバ

6
「フェイルファスト」プロシージャは防御プログラミングの1つの形式であると私は主張します。
Ryan Delucchi、2009年

6
@ryanは正解です。フェイルファストは優れた防御コンセプトです。あなたがいる状態が不可能である場合は、足を引きずり続けないでください、速くて大声で失敗してください!メタデータ駆動型の場合は、さらに重要です。ディフェンシブプログラミングは単にパラメーターをチェックするだけではありません...
Bill K

75

SQL

データを削除する必要があるときは、

select *    
--delete    
From mytable    
Where ...

私がそれを実行すると、where句を忘れたか、または失敗したかどうかがわかります。安全です。すべて問題なければ、「-」コメントトークンの後のすべてを強調表示して実行します。

編集:大量のデータを削除する場合は、*の代わりにcount(*)を使用します


これをiPhoneで書いたのですが、テキストを選択できません。誰かが私のコードをコードとしてマークできれば、本当にありがたいです。ありがとう。
ジョンマッキンタイア

6
私はそれをトランザクションにラップするだけで、
失敗

36
トランザクションの開始| ROLLBACK TRANSACTIONを
ダリンSeivewright

3
+1はい、これはトランザクションに置き換えることができると思いますが、私はこのように簡単に実行できることが好きです。
Ryan Delucchi、2009年

3
トランザクションを使用することをお勧めします。これは、何十回も実行して実際の効果を確認し、正しく取得してコミットできることが確実になるまで確認できることを意味します。
Ryan Lundy

48

アプリケーションの起動時に適切なメモリチャンクを割り当てます。SteveMcConnellがこれをコードコンプリートのメモリパラシュートと呼んだと思います。

これは、重大な問題が発生して終了する必要がある場合に使用できます。

このメモリを事前に割り当てると、セーフティネットが提供されます。メモリを解放し、使用可能なメモリを使用して次のことを実行できます。

  • すべての永続データを保存します
  • 適切なファイルをすべて閉じます
  • エラーメッセージをログファイルに書き込む
  • ユーザーに意味のあるエラーを提示する

私はこれが雨の日の基金と呼ばれるのを見ました。
台座、

42

デフォルトのケースがないすべてのswitchステートメントに、エラーメッセージでプログラムを中止するケースを追加します。

#define INVALID_SWITCH_VALUE 0

switch (x) {
case 1:
  // ...
  break;
case 2:
  // ...
  break;
case 3:
  // ...
  break;
default:
  assert(INVALID_SWITCH_VALUE);
}

2
または、現代の複雑な言語でのスロー。
トム・ホーティン-タックライン09

2
アサートには、コンパイル時にその影響をグローバルに無効にできるという利点があります。ただし、言語でサポートされている場合は、スローの方が適切な場合があります。
Diomidis Spinellis

2
Assertには、本番コードで役立つ別の利点があります。問題が発生した場合、何が失敗したか、プログラムのどの行からエラーが発生したかが正確にわかります。そのようなバグレポートは素晴らしいです!
メイソンウィーラー、

2
@Diomidis:別の見方:Assertには、コンパイル時にその効果をグローバルに無効にできるという欠点があります。
2019

1
スローは確かに優れています。そして、テスト範囲が十分であることを確認します。
ドミニククロニン

41

列挙型のさまざまな状態を処理する場合(C#):

enum AccountType
{
    Savings,
    Checking,
    MoneyMarket
}

次に、いくつかのルーチンの中で...

switch (accountType)
{
    case AccountType.Checking:
        // do something

    case AccountType.Savings:
        // do something else

    case AccountType.MoneyMarket:
        // do some other thing

    default:
-->     Debug.Fail("Invalid account type.");
}

ある時点で、この列挙型に別のアカウントタイプを追加します。そして私がそうするとき、私はこのswitch文を修正することを忘れます。そのため、Debug.Failこの事実に注意を向けさせるために、(デバッグモードで)恐ろしくクラッシュします。を追加するcase AccountType.MyNewAccountType:と、恐ろしいクラッシュが停止します。さらに別の種類のアカウントを追加して、ここでケースを更新するのを忘れるまで。

(はい、ここでは多態性がおそらくより優れていますが、これは私の頭の上の例にすぎません。)


4
ほとんどのコンパイラは、caseブロックで一部の列挙型を処理しない場合に警告を発行するのに十分スマートです。ただし、デフォルトを失敗に設定することは依然として適切な形式です。列挙型は単なる数値であり、メモリ破損が発生した場合、無効な値が表示される可能性があります。
Adam Hawes、

cでは、最後にinvalidAccountTypeを追加していました。これは時々役に立ちます。
Ray Tayek、2009年

1
@Adam- すべてを再コンパイルすると、コンパイラーは警告を出します。新しいクラスを追加し、部分的にのみ再コンパイルする場合、上記のようなことに気付かない場合があり、デフォルトのケースで節約できます。静かに失敗するだけではありません。
Eddie、

4
切れ目はコメントで暗示されている、スラシェン。:P
ライアンランディ

2
デバッグ後は、プロダクションコードの「throw new NotSupportedException()」にする必要があります。
user7116 2009年

35

文字列(特にユーザー入力に依存する文字列)を含むエラーメッセージを出力するときは、常に一重引用符を使用します''。例えば:

FILE *fp = fopen(filename, "r");
if(fp == NULL) {
    fprintf(stderr, "ERROR: Could not open file %s\n", filename);
    return false;
}

%sたとえば、ファイル名が空の文字列または空白文字などであるため、引用符の欠如は本当に悪いです。出力されるメッセージはもちろん次のようになります。

ERROR: Could not open file

だから、常に行う方が良い:

fprintf(stderr, "ERROR: Could not open file '%s'\n", filename);

次に、少なくともユーザーにはこれが表示されます。

ERROR: Could not open file ''

これにより、エンドユーザーから提出されたバグレポートの品質が大きく異なることがわかりました。一般的なサウンドではなく、このようなおかしなエラーメッセージが表示される場合は、単に「ファイルを開けない」と書くのではなく、コピー/貼り付けする可能性が高くなります。


4
良い問題、私もこの問題を見てきました
Alex Baranosky、2009年

28

SQLの安全性

データを変更するSQLを記述する前に、すべてをロールバックされたトランザクションにラップします。

BEGIN TRANSACTION
-- LOTS OF SCARY SQL HERE LIKE
-- DELETE FROM ORDER INNER JOIN SUBSCRIBER ON ORDER.SUBSCRIBER_ID = SUBSCRIBER.ID
ROLLBACK TRANSACTION

これにより、不正な削除/更新を永久に実行することが防止されます。そして、全体を実行して妥当なレコード数を確認したりSELECT、SQLとの間にステートメントを追加ROLLBACK TRANSACTIONして、すべてが正しく見えることを確認できます。

期待どおりに機能することが完全にわかったら、を変更しROLLBACKCOMMIT実際に実行します。


あなたはこれにパンチを打つために私を倒しました!:-)
ハワードピンズリー2009年

2
私はいつもこれを行っていましたが、DBクラスターで非常に多くのオーバーヘッドが発生するため、停止する必要がありました。
Zan Lynx

25

すべての言語:

変数のスコープを必要最小限に減らします。それらを次のステートメントに運ぶために提供される変数エスキューします。存在しない変数は、理解する必要のない変数であり、責任を負うことはできません。同じ理由で、可能な限りLambdaを使用してください。


5
正確にどういう意味ですか?次の行までしか存在しない変数を導入することもあります。これらは式の名前として機能するため、コードが読みやすくなります。
zoul 09年

4
はい、私も同意しません。非常に複雑な式では、一時変数を使用して、式を2つ、場合によってはより短くより単純なものに分割することをお勧めします。メンテナンスの際にエラーが発生しにくくなり、コンパイラーは
温度

1
わかりやすくするために(また、デバッグ用のターゲットを作成するために)変数を宣言する例外的なケースがあることに同意します。私の経験では、一般的な慣習は反対方向に誤ることです。
dkretz 2009年

私は両方の見解に同意します。式を一時変数に分割するときは、別のスコープ内で分割しようとします。ヘルパーメソッドと同様に、ラムダはこれに最適です。
エリックフォーブス

19

疑わしい場合は、アプリケーションを爆撃してください!

チェック一人ひとりの先頭にパラメータ一人ひとりコードへの前提条件がある場合は、正しい例外および/または意味のあるエラーメッセージを表示して方法(明示的にそれを自分でコーディング、または契約ベースのプログラミングを使用すると、ここでは関係ないか)と爆弾を満たされていません。

コードを記述するときにこれらの暗黙の前提条件について知っていますが、明示的にチェックされていない場合は、後で問題が発生したときに自分用の迷路を作成し、数十のメソッド呼び出しのスタックが症状の発生と実際の場所を分離します前提条件が満たされていない場合(=問題/バグが実際にある場合)。


そしてもちろん:これを簡単にするために、汎用コード(小さなライブラリ、C#の拡張メソッドなど)を使用します。そのため、param.NotNull("param")代わりにどこか薄いglikeを書くことができますif ( param == null ) throw new ArgumentNullException("param");
peSHIr

2
または、Spec#のような契約ベースのプログラミングを使用してください!
bzlm 2009年

アプリケーションによって異なります。フライバイワイヤ航空機やペースメーカーのコードを書いている人たちがpeSHIrのように思わないことを願っています。
MarkJ、2009

6
@MarkJ:あなたは本当にそれを理解していませんね?早期爆撃(開発中およびテスト中)の場合、本番稼働中は爆撃しないでください。だから私は彼らこのようにプログラムすることを本当に望みます
peSHIr 2009

特にプライベートメソッドと保護されたメソッドについては、あまり好きではないことを認めなければなりません。理由:a)ビジネス要件にまったく似ていないチェックでコードが散らかっているb)これらのチェックは非公開メソッドをテストするのが難しいc)多くの場合それは役に立たない、たとえばnull値はメソッドが失敗するためとにかく2行後
Erich Kitzmueller、2010

18

Javaでは、特にコレクションでは、APIを使用するため、メソッドがリスト型(たとえば)を返す場合は、以下を試してください。

public List<T> getList() {
    return Collections.unmodifiableList(list);
}

クラスからエスケープする必要のないものを許可しないでください。


+1 C#には、読み取り専用のコレクションがあります。
tobsen 2009年

1
Javaの+1。私はいつもこれを使用しています
Fortyrunner 2009年

+1 ...私はこれをたくさんやります(もっと多くの人がこれを忘れないようにしたいのですが)。
Ryan Delucchi、2009年

基になるリスト変数のインスタンスが他にないことを確認してください。そうしないと、あまり効果がありません...
GreenieMeanie 2009年

5
参考までに、Collections.unmodifiableListは、不変のコピーではなく、リストの不変のビューを返します。したがって、元のリストが変更されると、ビューも変更されます!
ザルコネン2010

17

Perlでは、誰もが

use warnings;

好き

use warnings FATAL => 'all';

これにより、コードはコンパイラ/ランタイムの警告で停止します。これは主に、初期化されていない文字列をキャッチするのに役立ちます。

use warnings FATAL => 'all';
...
my $string = getStringVal(); # something bad happens;  returns 'undef'
print $string . "\n";        # code dies here

これがもう少し宣伝されて
いればいいのに

16

C#:

string myString = null;

if (myString.Equals("someValue")) // NullReferenceException...
{

}

if ("someValue".Equals(myString)) // Just false...
{

}

Javaでも同じで、おそらくほとんどのオブジェクト指向言語です。
MiniQuark 2009年

これはObjective-Cで便利です。nilにメッセージを送信することが可能です(特定のライセンスで「nullオブジェクトのメソッドを呼び出す」)。[nil isEqualToString:@ "Moo"]を呼び出すと、falseが返されます。
zoul 2009年

C#の例に同意しません。より良い解決策は、「if(myString == "someValue")」を使用することです。また、null参照の例外はなく、読みやすくなっています。
Dan C.

13
この例では、プログラムで潜在的に危険な状況を隠すことができます。nullであると予期していなかった場合は、例外をスローする必要があります。nullであると予期している場合は、そのように処理する必要があります。これは悪い習慣です。
rmeador 2009年

2
C#では、常にstring.Equals(<string1>、<string2>)を使用します。どちらかがnullであるかどうかは関係ありません。
ダーシーカッセルマン

15

string.IsNullOrEmptyのc#チェックで、長さ、indexOf、midなどの文字列に対して操作を実行する前に

public void SomeMethod(string myString)
{
   if(!string.IsNullOrEmpty(myString)) // same as myString != null && myString != string.Empty
   {                                   // Also implies that myString.Length == 0
     //Do something with string
   }
}

[編集]
.NET 4.0で次のこともできるようになりました。これにより、値が空白のみかどうかがさらにチェックされます

string.IsNullOrWhiteSpace(myString)

7
それは防御的ではなく、問題を無視しています。する必要がありますif (!string.IsNullOrEmpty(myString)) throw new ArgumentException("<something>", "myString"); /* do something with string */
enashnash

14

JavaおよびC#では、すべてのスレッドに意味のある名前を付けます。これには、スレッドプールスレッドが含まれます。スタックダンプをより意味のあるものにします。スレッドプールスレッドにも意味のある名前を付けるには少し手間がかかりますが、1つのスレッドプールで長時間実行されるアプリケーションに問題がある場合、スタックダンプが発生する可能性があります(SendSignal.exeについて知っていますよね? )、ログを取得します。実行中のシステムを中断する必要なく、どのスレッドが何であるかを知ることができます。問題が何であれ、行き詰まり、漏れ、成長など。


そして-Windowsでは-C ++でも!(特別なSEH例外スローとそれに続くキャッチで有効化されます)。
Brian Haak 2013年

12

VB.NETを使用して、Visual Studio全体でOption ExplicitとOption Strictをデフォルトでオンにします。


私は古いコードで作業しているので、厳密なオプションをオンにしていないので(修正するにはコンパイラエラーが多すぎるため)、これらのオプションを両方ともオンにすることで(私の場合は)、かなりの節約になります。痛み。
Kibbee、2009年

1
ちなみに、Option Strictを使用していない古いコードを変換して使用する場合は、Stringに自動変換されたアイテムの場合、ToString()を使用していないことに注意してください。彼らは文字列へのキャストを使用しています。私の初期の.NETの時代には、これらをToString()を使用するように変更しました。
Ryan Lundy

10

C ++

#define SAFE_DELETE(pPtr)   { delete pPtr; pPtr = NULL; }
#define SAFE_DELETE_ARRAY(pPtr) { delete [] pPtr; pPtr = NULL }

次に、すべての ' delete pPtr 'および ' delete [] pPtr '呼び出しをSAFE_DELETE(pPtr)およびSAFE_DELETE_ARRAY(pPtr)で置き換えます

誤って削除後にポインタ「pPtr」を使用すると、「アクセス違反」エラーが発生します。ランダムなメモリ破損よりも修正がはるかに簡単です。


1
テンプレートを使用することをお勧めします。スコープとオーバーロードが可能です。

私はまさにそれを言っていました。マクロの代わりにテンプレートを使用します。そうすれば、コードをステップ実行する必要がある場合でも実行できます。
スティーブロウ

私は学校で難しい方法をデバッグする方が簡単だったことを学びました。それは私が犯していない間違いです。:)
グレッグD

3
または、スマートポインターを使用します。または、いやいや、とにかくできる限り新規/削除を避けます。
アラファンギオン2009年

1
@Arafangion:避けてくださいdeletenew新しいオブジェクトがスマートポインタによって所有される限り、使用は問題ありません。
アレクサンドルC.

10

Javaでは、アサーションをオフにして本番用コードを実行した場合でも、assertキーワードを使用すると便利です。

private Object someHelperFunction(Object param)
{
    assert param != null : "Param must be set by the client";

    return blahBlah(param);
}

アサーションをオフにしても、少なくともコードはparamがどこかに設定されることが予想されるという事実を文書化します。これはプライベートヘルパー関数であり、パブリックAPIのメンバーではないことに注意してください。このメソッドはあなたしか呼び出せないので、それがどのように使用されるかについて特定の仮定をすることは問題ありません。パブリックメソッドの場合は、無効な入力に対して実際の例外をスローする方が良いでしょう。


.NETでは、Debug.Assertが同じことを行います。あなたが考える場所を持っているときはいつでも、あなたはそれがあればそうすることを、そこDebug.Assertのを置くことができる「この参照は、右?、ここではnullにすることはできません」ができ nullの場合も、あなたはどちらかのバグを修正したり、仮定を変更することができます。
ライアンランディ

1
+1、パブリックメソッドは契約の失敗をスローし、プライベートはアサートする必要がある
user7116 2009年

9

readonlyReSharperが見つかるまでキーワードは見つかりませんでしたが、特にサービスクラスでは直感的に使用しています。

readonly var prodSVC = new ProductService();

可能なすべてのフィールドで、Javaの同等の「最終」キーワードを使用しています。これにより、フィールドを設定しない、またはフィールドを複数回設定できる混乱するスイッチを作成するなど、骨頭を動かす必要がなくなります。ローカルの変数やパラメータにマークを付ける可能性は低いですが、害はないと思います。
アウトロープログラマ、

C#では、ローカル変数を読み取り専用としてマークすることはできないため、選択することさえできません...
Jason Punyon

ここでの読み取り専用の意味はわかりませんが、Javaの最終版では不十分です。オブジェクトへのポインタが変更されていないことを意味しますが、オブジェクト自体の変更を防ぐ方法はありません。
Slartibartfast 2009年

C#では、読み取り専用の構造体が変更されるのを防ぎます。
サミュエル

9

Javaでは、何かが発生していて、その理由がわからない場合、Log4Jを次のように使用することがあります。

if (some bad condition) {
    log.error("a bad thing happened", new Exception("Let's see how we got here"));
}

このようにして、予期しない状況になった方法を示すスタックトレースを取得します。たとえば、ロックが解除されないロック、nullにできないnullなどです。明らかに、実際の例外がスローされた場合、私はこれを行う必要はありません。これは、実際に何も邪魔することなく、製品コードで何が起こっているかを確認する必要があるときです。私はしていない例外をスローするようにしたいと私は1つをキャッチしませんでした。スタックトレースに適切なメッセージを記録して、何が起こっているのかを知らせたいだけです。


うーん、これはちょっとすっきりしていますが、機能コードとエラー処理コードが混ざってしまうのではないかと心配しています。try-catchメカニズムは不器用な側面にある場合がありますが、1つのブロック(try)から別のブロック(catch)への例外ルート変更の実行を強制します。ここですべてのエラー処理が行われます
Ryan Delucchi

1
これはエラー処理には使用されませんが、診断にのみ使用されます。これにより、コードのフローを中断することなく、コードが予期しない状況になったパスを確認できます。これは、メソッド自体が予期しない状況を処理できるときに使用しますが、それでも発生しないはずです。
Eddie、

2
new Exception( "message")。printStackTrace();を使用することもできます。スローやキャッチは必要ありませんが、ログにスタックトレースが表示されます。明らかに、これは本番用コードには含まれていませんが、デバッグには非常に役立ちます。
ジョーン

9

Visual C ++を使用している場合は、基本クラスのメソッドをオーバーライドするときは常にoverrideキーワードを使用してください。これにより、基本クラスのシグネチャが変更された場合に、間違ったメソッドが暗黙的に呼び出されるのではなく、コンパイラエラーがスローされます。もしそれが以前に存在していたなら、これは私を数回救ったでしょう。

例:

class Foo
{
   virtual void DoSomething();
}

class Bar: public Foo
{
   void DoSomething() override { /* do something */ }
}

Javaの場合、これはクラスFoo {void doSomething(){}}クラスBar {@Override void doSomething(){}}アノテーションがない場合に警告を表示します(そうしないと、暗黙的にメソッドをオーバーライドできません)と)アノテーションが存在するが、何もオーバーライドしない場合。
Jorn

いいですね...これについては知りませんでした...残念ながら、Microsoft固有のようです...しかし、いくつかの優れた
#defineは

8

私はJava で、ロックが解除されるまで無期限に待機することはほとんどないことを学びました。現実的には、ロックは数秒以内に解除されるはずです。その後、一定の時間だけ待機します。ロックがロック解除されない場合、私は不平を言ってスタックをログにダンプし、システムの安定性に最適なものに応じて、ロックがロック解除されたかのように続行するか、ロックがロック解除されなかったかのように続行します。

これは、これを始める前に神秘的だったいくつかの競合状態と疑似デッドロック状態を分離するのに役立ちました。


1
このようにタイムアウトを使用して待機することは、他のさらに悪い問題の兆候です。
dicroce、2009年

2
稼働時間を長くする必要があるシステムでは、少なくとも診断情報を取得して問題を見つけられるようにすることをお勧めします。システムが既知の状態にあるときに、スレッドを終了するか、呼び出し元に応答を返すことを好み、セマフォで待機する場合があります。しかし、あなたは永遠にハングアップしたくありません
Eddie

8

C#

  • パブリックメソッドの参照型パラメーターのnull以外の値を確認してください。
  • sealed必要のない場所に依存関係を導入しないようにするために、クラスに多くを使用しています。継承を許可することは、偶然ではなく明示的に行う必要があります。

8

エラーメッセージを発行するときは、少なくとも、プログラムがエラーをスローすることを決定したときと同じ情報を提供するようにしてください。

「Permission denied」は、アクセス権の問題があったことを示していますが、問題が発生した理由や場所がわかりません。「トランザクションログ/ my / fileを書き込めません:読み取り専用ファイルシステム」は、たとえそれが間違っていても、特に間違っていたとしても、決定が下された根拠を知ることができます:間違ったファイル名?間違って開いた?その他の予期しないエラー?-そして、問題があったときにどこにいたかを知らせます。


1
エラーメッセージのコードを書くときは、メッセージを読み、次に知りたいことを尋ね、追加します。不合理になるまで繰り返します。例:「範囲外」。範囲外とは何ですか?「人数が範囲外です。」値は何でしたか?「人数(42)が範囲外です。」範囲は何ですか?「人数(42)が範囲外(549〜666)。」
HABO 2017年

7

C#では、asキーワードを使用してキャストします。

string a = (string)obj

objが文字列でない場合、例外をスローします

string a = obj as string

objが文字列でない場合、aはnullのままになります

それでもnullを考慮する必要がありますが、通常はキャスト例外を探すよりも簡単です。「キャストまたはブローアップ」タイプの動作が必要な場合があり(string)objます。この場合、構文が推奨されます。

私のコードでは、as約75%の(cast)構文と約25%の構文を使用しています。


正解ですが、参照を使用する前にnullを確認する必要があります。
ブライアンラスムッセン

7
わかりませんでした。ヌルを好むというのは私には悪い決断のようです。ランタイム中にどこかに問題が発生し、元の理由へのヒントはありません。
Kai Huppmann、2009年

はい。これは、正しい型ではないときに実際にnullが必要な場合にのみ役立ちます。場合によっては便利です。コントロールロジックとデザインが分離されていることが多いSilverlightでは、ロジックがボタンである場合にのみ、コントロールを「上」にしたいと考えます。存在しない場合は、存在しない(= null)かのようになります。
サンダー

2
これは要塞の大宴会場の窓と同じくらい防御力があるようです。しかし、それは美しい眺めです!
Pontus Gagge 2009年

1
これは、「プログラムを壊した」ために例外を使用しなかった人を思い出させます。オブジェクトが期待するクラスではないときに特定の動作が必要な場合は、別の方法でコーディングすると思います。is/instanceof心に春。
キルシュシュタイン2009

6

以下のために準備される任意の入力、および、ログにダンプ予想外のことであるあなたが得る任意の入力。(理由の範囲内。ユーザーからパスワードを読み取っている場合は、それをログにダンプしないでください。また、1秒間に数千のこの種のメッセージをログに記録しないでください。ログを記録する前の内容と可能性および頻度に関する理由。)

ユーザー入力の検証について話しているだけではありません。たとえば、XMLが含まれると予想されるHTTPリクエストを読み取る場合は、他のデータ形式に備えてください。XMLのみを期待しているHTML応答を見て驚いた-リクエストを見て気づかなかった透過的なプロキシを経由していて、顧客が無知だと主張していることを確認して、プロキシがタイムアウトを完了しようとしてタイムアウトしたリクエスト。したがって、プロキシはHTMLエラーページをクライアントに返し、XMLデータのみを予期していたクライアントを混乱させていました。

したがって、回線の両端を制御していると考えている場合でも、悪意を伴うことなく、予期しないデータ形式を取得できます。準備し、防御的にコーディングし、予期しない入力があった場合に診断出力を提供します。


6

Design by Contractアプローチを使用してみます。どの言語でもランタイムをエミュレートできます。すべての言語で「アサート」がサポートされていますが、より便利な方法でエラーを管理できるより優れた実装を作成するのは簡単で便利です。

トップ25最も危険なプログラミングエラー「不適切な入力の検証は、」セクション「コンポーネント間の安全でない相互作用」の中で最も危険な間違いです。

メソッドの最初に前提条件アサートを追加することは、パラメーターが一貫していることを確認するための良い方法です。メソッドの終わりに、私は書く事後条件をチェック出力するintededているものであることをことを、。

不変条件を実装するために、「クラスの一貫性」をチェックする任意のクラスにメソッドを記述します。これは、事前条件マクロと事後条件マクロによって自動的に呼び出される必要があります。

私はコード契約ライブラリを評価しています


6

ジャワ

Java APIには不変オブジェクトの概念がありません。これは悪いことです!その場合、Finalが役立ちます。不変のすべてのクラスにfinalのタグを付け、それに応じてクラスを準備します。

ローカル変数でfinalを使用して、値が変更されないことを確認すると便利な場合があります。これは醜いが、必要なループ構造で便利であることがわかりました。それが定数であることが修正されていても、誤って変数を再利用するのは簡単です。

ゲッターで防御コピーを使用します。プリミティブ型または不変オブジェクトを返さない限り、カプセル化に違反しないようにオブジェクトをコピーしてください。

クローンは使用せず、コピーコンストラクターを使用してください。

equalsとhashCodeの間の規約について学びます。これは頻繁に違反されます。問題は、99%のケースでコードに影響を与えないことです。人々はイコールを上書きしますが、hashCodeは気にしません。コードが壊れたり奇妙な動作をする可能性のあるインスタンスがあります。たとえば、可変オブジェクトをマップのキーとして使用します。


5

echoPHPで何度も書くのを忘れました。

<td><?php $foo->bar->baz(); ?></td>
<!-- should have been -->
<td><?php echo $foo->bar->baz(); ?></td>

実際にエコーしていなかったのに、なぜ-> baz()が何も返さなかった理由を突き止めて理解するには、永遠に時間がかかります。:-Sしたがって、EchoMeエコーする必要のある任意の値をラップできるクラスを作成しました。

<?php
class EchoMe {
  private $str;
  private $printed = false;
  function __construct($value) {
    $this->str = strval($value);
  }
  function __toString() {
    $this->printed = true;
    return $this->str;
  }
  function __destruct() {
    if($this->printed !== true)
      throw new Exception("String '$this->str' was never printed");
  }
}

そして、開発環境では、EchoMeを使用して、印刷すべきものをラップしました。

function baz() {
  $value = [...calculations...]
  if(DEBUG)
    return EchoMe($value);
  return $value;
}

その手法を使用すると、最初にが欠けている例でecho例外がスローされます...


デストラクタは$ this-> printed!== trueをチェックしませんか?
カルステン

テンプレートシステムの使用を検討する必要があります。phpをhtmlに埋め込むのは、ほとんどすべてのシステムで最適ではありません。
Cruachan、

多分私は何かが足りないのですか?:しかし、それはこのように私には見えますがコーディングのために補償しようとしているAnObject.ToString代わりにWriteln(AnObject.ToString)
幻滅

はい、しかし間違いはPHPではるかに簡単に
起こり

4

C ++

newと入力したら、すぐにdeleteと入力する必要があります。特にアレイの場合。

C#

特にMediatorパターンを使用する場合は、プロパティにアクセスする前にnullを確認してください。オブジェクトは渡され(そして、すでに述べたように、asを使用してキャストする必要があります)、nullに対してチェックします。nullにならないと思ってもとにかくチェックしてください。びっくりしました。


私はあなたの最初の点が好きです。たとえば、何かのコレクションを返すメソッドを書くときも、同様のことをします。最初の行にコレクションを作成し、すぐにreturnステートメントを記述します。あとは、コレクションの入力方法を入力するだけです。
アウトロープログラマ

5
C ++では、newと入力したときに、そのポインターをAutoPtrまたは参照カウントコンテナーにすぐに割り当てる必要があります。C ++にはデストラクタとテンプレートがあります。それらを賢く使用して、削除を自動的に処理します。
An̲̳̳drew 2009年

4

動的なランタイムログレベル調整を可能にするログシステムを使用します。ロギングを有効にするためにプログラムを停止する必要がある場合はよくありますが、バグが発生したまれな状態はすべて失われます。プロセスを停止せずに、より多くのロギング情報をオンにできる必要があります。

また、Linuxの 'strace -p [pid]'は、プロセス(またはLinuxスレッド)が実行しているシステムコールが必要であることを示します。最初は奇妙に見えるかもしれませんが、libcの呼び出しによって一般的に行われるシステムコールに慣れると、フィールド診断でこれが非常に役立つことがわかります。

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