6

このコードで何が例外を発生させる可能性があるのでしょうか。

function CreateBibleNames: TStrings;
begin
  Result := TStringList.Create;
  try
    Result.Add('Adam');
    Result.Add('Eva');
    Result.Add('Kain');
    Result.Add('Abel');
  except
    Result.Free;
    raise;
  end;      
end;

私は Delphi を使用しているので、おそらく一度は例外処理を使用したことがあります。上記のコードは熟練したプログラマーによって書かれたものであり、例外が冗長であるとは思いません。それでも、この概念で例外処理を使用することは、私にとって謎のままです。安全なコードのようです (end 以外は try なし)。私はこのような類似のコード スニペットを何度も見てきました。そのため、私の経験にもかかわらず、このように記述する十分な理由があり、それが必要であるとは証明されませんでした。

さらに、何かが失敗すると、例外の説明が表示されます....

ありがとう

4

8 に答える 8

12

わかりました、そのコードは奇妙です、私は同意します、そしてなぜそれがそのように書かれたのか完全に理解しています. しかし、コードの根底にある前提が間違っているため、そのように書かれています。構造が奇妙に見えるという事実は「コードのにおい」であり、何かが最善の方法で行われていない可能性があることを示しているはずです。

まず、try...except ブロックの通常とは異なる構文が使用される理由は次のとおりです。関数は TStringList を作成しますが、それを埋める過程で何か問題が発生した場合、作成された TStringList はヒープ上で「失われ」、メモリ リークが発生します。そのため、元のプログラマーは守備的で、例外が発生した場合に TStringList を解放してから、例外が再び発生するようにしました。

さて、ここで「悪い前提」の部分です。関数は TStrings のインスタンスを返しています。これが常に最善の方法であるとは限りません。そのようなオブジェクトのインスタンスを返すと、「私が作成したものを誰が処分するのか?」という疑問が生じます。これにより、呼び出し側で TStrings インスタンスが割り当てられていることを簡単に忘れてしまう状況が発生します。

ここでの「ベター プラクティス」は、関数がパラメータとして TStrings を取り、既存のインスタンスを埋めることです。このように、誰がインスタンス (呼び出し元) を所有しているか、したがって誰がその存続期間を管理する必要があるかについて疑いの余地はありません。

したがって、関数はプロシージャになり、次のようになります。

procedure CreateBibleNames(aStrings: TStrings);
begin
  if aStrings <> nil then
  begin
    aStrings .Add('Adam');
    aStrings .Add('Eva');
    aStrings .Add('Kain');
    aStrings .Add('Abel');
  end;      
end;

これは、毎回オブジェクト インスタンスを返すことが悪いことだと言っているのではありません。たとえば、これは非常に便利な Factory パターンの唯一の機能です。しかし、このような「一般的な」機能については、上記の方法が「より良い」方法です。

于 2009-12-13T05:23:31.893 に答える
9

関数から新しく構築されたオブジェクトを返すときは、良い習慣です。文字列リストよりも複雑なシナリオでは、一部の不変条件が壊れているか、その他のエラーが発生して例外が発生する可能性があり、その場合は戻り値を解放する必要があります。これが発生する可能性のあるすべての状況を調べる必要があるのではなく、新しく構築されたオブジェクトを返すための単一のパターンを用意し、どこでもそれに従うことをお勧めします。

そうすれば、構築しているオブジェクトの実装が将来変更されたときに、例外がスローされても安全です。

于 2009-12-13T05:15:07.117 に答える
4

いくつかの理由から、私はそのようにコードを書きます:

1) メモリ割り当てがどのように見えるかを標準化することにより、すべての行を読まなくても、ソース コードを調べて何かが欠けているかどうかを簡単に調べることができます。ここでは、.Create が表示されるだけで、例外処理が適切であることがわかります。そのため、try...except の間の部分を読む必要はありません。

2) メモリ割り当てのコーディング方法を標準化することで、必要な場合でも忘れることはありません。

3) 後でソース コードを変更したり、コードを挿入したり、.Add の動作を変更したり、TStringList を別のものに置き換えたりしても、このコードは壊れません。

4) これは、TStringList.Add() が例外をスローするかどうかを考える必要がないことを意味し、より多くの価値を提供する他の作業のために脳を解放します。

于 2009-12-13T09:08:45.937 に答える
4

私が見ることができる例外の唯一の潜在的な理由は、OutOfMemory 例外であり、これが事実である場合、いずれにせよすべての賭けは無効になります。私の意見では、これは有効な概念の乱用の例です。

try except を過度かつ不当に使用すると、コードが乱雑になり、読みにくくなります。

于 2009-12-12T23:13:23.390 に答える
2

この種のコードは、古典的な「ファクトリー」パターンです。この例は些細なことであり、極端なコーナー ケースで例外が発生する可能性があるため、例外処理を必要としない場合があります。しかし、ファクトリ コードがはるかに複雑な場合は、以前に作成されたインスタンスを解放するのが正しいです。例外が発生する前にインスタンスが作成されたかどうかを呼び出し元が認識できないため、発生した例外を再発生させます。例外が返された場合は、インスタンスが作成または解放されていないと想定する方がはるかに適切です。ファクトリは、返されるインスタンスが常に同じであるとは限らない場合 (つまり、特定の階層内の任意のクラス)、およびインスタンスを作成するためのパラメータが呼び出し元には "unnonwn" であるがファクトリには不明な場合に役立ちます。このような場合、呼び出し元は、適切なパラメーターで「満たす」ためだけに作成済みのインスタンスを渡すことはできませんが、インスタンスを必要とするだけです。もちろん、呼び出し元が作成されたインスタンスの「所有者」になることを明確にする必要があります。 .

于 2009-12-13T14:40:03.863 に答える
2

TStringList のソースまたは信頼できるドキュメントがなければ、実際に知ることはできません。説明のために、TStringList が、メモリが不足したときにリストの一部をディスクとの間でスワップし始めるように記述されていると仮定して、ディスクと同じ大きさのリストを管理できるようにします。現在、呼び出し元は、ディスク容量不足、不良ブロックの発生、ネットワーク タイムアウトなど、さまざまな I/O エラーの影響を受けやすくなっています。

そのような例外を処理するのはやり過ぎですか? シナリオに基づくジャッジメントコール。NASA のプログラマーに意見を聞くことができます。

于 2009-12-13T03:21:39.297 に答える
2

最も明らかな例外は、Delphi がOutOfMemoryException. いくつかの文字列をリストに追加するのに十分なメモリがない場合は、リスト全体を解放し、例外を再スローします。

これは、あらかじめ決められた名前のリストを作成するための過度に防御的なプログラミングのように思えます。通常、メモリ不足による例外が 1 つ発生すると、アプリケーション全体が停止します。

于 2009-12-12T23:07:24.310 に答える
-2

興味深い補足として、Java の作成者の一部は、事実上すべてのケースで例外のキャッチを要求するという決定は間違いであると考えています。これは、ほとんどの例外が実際にプログラムを強制終了する結果となるためです。したがって、プログラマーに例外を強制的にキャッチさせて、何かを出力して強制終了させるのではなく、ランタイム システムにプログラムを強制終了させて​​スタック トレースを出力させた方がよいでしょう。プログラム。

于 2009-12-12T23:16:40.590 に答える