2

私が管理しているいくつかのアプリの多くの場所で、begin/end の使用を回避するor文でtry/finallyortry/exceptブロックを使用するコードを見つけました。for loopif

次のコードを検討してください (本番コードではなく、単なるサンプルです)

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Classes,
  SysUtils;
    
Procedure TestNoBeginEnd;
var
 i   : Integer;
 L1  : TStringList;
begin
    for i := 1 to 10 do
    try
      L1:=TStringList.Create;
      try
        L1.Add('Bar');
        L1.Add(IntToStr(i));
        L1.Add('Foo');
      finally
        Writeln(L1.Text);
        L1.Free;
      end;
    except
      on E: Exception do
        Writeln('Opps '+E.ClassName, ': ', E.Message);
    end;
end;

begin
  try
    TestNoBeginEnd;

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

質問、デルファイで begin/end の代わりに try/finally または try/except を使用すると、悪い習慣、コードの匂い、または欠点が存在すると見なされますか?

アップデート

ばかげたサンプル コードで申し訳ありませんが、try/finally と try/except を明確にするためだけに begin/end を置き換えるふりをしません。 try/finally または try/except の開始/終了は必要ありません。

4

4 に答える 4

6

特にbegin..endの直前または. try..finally/except..endtryend

そうは言っても、問題は、命令が1つしかない場合(あなたの場合は命令)でも体系的begin..endに必要かどうか..doです。..thentry..

それは主にスタイルと習慣の問題であり、防衛的かつ体系的に開始..終了を配置することを好む人もいれば、不要な行をできるだけ避けようとする人もいます。

begin..endかなりの数の行が含まれる場合、スコープをより明確にするためにを追加する傾向があります。

適切な判断を下して、後でコードを変更する誰か (またはあなた) が意図したスコープを見逃す可能性があるかどうかを確認してください。

于 2012-01-31T03:22:32.550 に答える
1

まず最初に。Begin/End は Try/Etc と同じではありません。後者は最初に一連の命令を示し、2 番目は単一の命令 (おそらく多くの命令を含む) を表します。

つまり、ハンマーとはしごのようなものです。さまざまな仕事のためのさまざまなツール。はしごで釘を打つことは確かですが、それは問題に対処する最も効率的な方法ではありません。

編集:質問を理解した場合(編集したので)、開始/終了なしで1つのtry/etcを使用できるかどうかを尋ねています。はい、try/etc は単一のステートメントであるため、これを行うことができます。実際、あまり役に立たないコードを避けるのはおそらく良いことだと私は言います。

編集前:

それで、それはどういう意味ですか?つまり、その関数でエラーが発生した場合、ログに記録されますが、基本的に無視されます。

本当に優れた例外処理があり、コードが例外をエスカレートするか正しく回復しない限り、これは悪い習慣だったと思います

あなたのサンプルでは、​​例外ハンドラーが行うことはすべて Opps (oops?) と言うだけです。それは何が良いですか?確かにこれはおそらくサンプルにすぎないので、コードを調べて実際に何をするかを確認します。これを書いた人は一般的にエラーを処理しようとしていたと思いますが、実際にはこのパターンを使用してコードの信頼性をはるかに悪化させています.

私は、「例外ハンドラーは例外的な状況のためのものです」という一般的なケースを使用します。ここでは、回復できるか、誰か/物事に知らせる必要があります。ほとんどの場合、誰もエラーを処理できず、システムは設計どおりに低下する必要があります。これは、例外ハンドラーが安全にログを記録し、例外を渡す場所です。

後者のケースは、すべての「システム内の大きな境界」に拡張します。つまり、サービス/API 境界で例外を収集し、適切にログ/ラップします。

(悪い API は、例外的ではない状況で例外を使用する場合があることに注意してください。この場合、例外ハンドラーを安全に追加して、悪い設計をラップできます)。

于 2012-01-31T02:52:49.190 に答える
1

質問に対するあなたの編集により、質問が根本的に変わりました。少なくともタイトルを変更する必要がありましたが、今のところ、質問がまったく救済可能かどうか疑問に思っています (-1)。

そうは言っても、元の質問に対処するとき:

Begin/End ステートメントで十分な場合は、Try/Finally または Try/Except ステートメントを使用しないでください。Try ステートメントは、コンパイルされたコードに (小さな、または目立たない) 追加の命令を追加するという事実に加えて、そこにないコードの必要性を暗示しています。

for I := 0 to Count - 1 do
try
  Inc(Rect.Left, Shift);
finally
  Inc(Rect.Right, Shift);
end;

それをしないでください。

于 2012-01-31T06:07:33.840 に答える
0

try finally/except が同じコードをブロックにラップするときに begin end を省略しても問題はありません。try ブロックには begin ブロックよりも多くのオーバーヘッドがあることを覚えておいてください。このオーバーヘッドは通常は無視できますが、ループ、特に繰り返し参照されるメソッド内のループでは、意味のある違いが生じることがわかりました。

上記の詳細を念頭に置くと、ループ内で不要なオーバーヘッドが繰り返し追加されるため、この例はやや貧弱です。try ブロックをループの外に移動できるときはいつでも、それは良いことです。あなたの例では、ループの反復ごとに例外をキャッチする機能が必要な場合は、以下のコードの方が効率的です。そうでない場合は、except ブロックも移動します。

Procedure TestNoBeginEnd;
var
 i   : Integer;
 L1  : TStringList;
begin
  L1:=TStringList.Create;
  try
    for i := 1 to 10 do
    try
      L1.Clear;
      L1.Add('Bar');
      L1.Add(IntToStr(i));
      L1.Add('Foo');
      Writeln(L1.Text);
      end;
    except
      on E: Exception do
        Writeln('Opps '+E.ClassName, ': ', E.Message);
    end;
  finally
    L1.Free;
  end;
end;
于 2012-01-31T18:46:15.167 に答える