4

私はリファクタリングしたいこのような関数を持っています

   function Myfunction(sUrl, sFile: String) : Boolean;
    var
      GetData : TFileStream;
    begin
      Result := False;
      //if the line below fails, I get an unhandled exception
      GetData := TFileStream.Create(sFile, fmOpenWrite or fmCreate);
      try        
        try
          IdHTTP.Get(sUrl, GetData);
          Result := (IdHTTP.ResponseCode = 200);
        except
          on E: Exception do begin
            MessageBox(0, PChar(E.message), 'Niðurhala skrá', MB_ICONERROR or MB_OK);
          end;
        end;
      finally
        GetData.Free;
      end;
    end;

    Procedure SomeOtherCode;
     Begin
        //How can I best defend against the unhandled exception above
        //unless the call to the function is packed in a try .. except block
        //the code jumps skips the if statement an goes to next 
        //exception block on the stack
        if MyFunction('http://domain.com/file.html', 'c:\folder\file.html') then
            ShowMessage('Got the file')
         else
            ShowMessage('Error !');
        End
     end;

質問:

上記の手順 SomeOtherCode 内のコメントを参照してください。

よろしくお願いします

4

6 に答える 6

10

try..exceptブロックで例外をトラップするコードをラップするだけです。

function MyFunction(...): Boolean;
var
  Stream: TFileStream;
begin
  Result := False;
  try
    Stream := TFileStream.Create(...);
    try
      // more code
      Result := ...
    finally
      Stream.Free;
    end;
  except
    // handle exception
  end
end;
于 2010-11-11T16:58:17.003 に答える
4

例外処理に関する要点は 2 つあります。

  • finallyリソースのクリーンアップ用です。これはビジネスロジックでよく見られます
  • except特定の例外に反応するためのものです(関数の結果と中間変数を介して状態ロジックを取り除きます); ビジネスロジックではほとんど見られません

あなたの場合:

Myfunctionブール値を返さない、exceptブロックを含まない、実行しないMessageBoxで例外を伝播させる必要があります。ブロックを含めて、何が問題なのかをユーザーに伝える
SomeOtherCode必要があります。except

例:

procedure Myfunction(sUrl, sFile: String);
var
  GetData: TFileStream;
begin
  Result := False;
  //if the line below fails, I get an unhandled exception
  GetData := TFileStream.Create(sFile, fmOpenWrite or fmCreate);
  try        
    IdHTTP.Get(sUrl, GetData);
    if (IdHTTP.ResponseCode <> 200) <> then
      raise Exception.CreateFmt('Download of %s failed, return code %d', [sURl, IdHTTP.ResponseCode]);
  finally
    GetData.Free;
  end;
end;

procedure SomeOtherCode:
begin
  try
    MyFunction('http://domain.com/file.html', 'c:\folder\file.html');
  except
    on E: Exception do begin
      MessageBox(0, PChar(E.message), 'Niðurhala skrá', MB_ICONERROR or MB_OK);
    end;
  end;
end;

これで、コードはよりきれいになりました。

  • ビジネス ロジックに UI はもうありません
  • exceptあなたが扱われている1つの場所
  • すべての障害は同等に処理されます ( cannot create filedownload failure)

これで頑張ってください。

--jeroen

于 2010-11-11T19:54:46.187 に答える
3

関数でユーザーにメッセージを表示し、失敗すると false を返すようにするには、次のようにコーディングします。

function Myfunction(sUrl, sFile: String) : Boolean;
var
  GetData : TFileStream;
begin
  Result := False;
  try
    //if the line below fails, I get an unhandled exception
    GetData := TFileStream.Create(sFile, fmOpenWrite or fmCreate);
    try        
      try
        IdHTTP.Get(sUrl, GetData);
        Result := (IdHTTP.ResponseCode = 200);
      except
        on E: Exception do begin
          MessageBox(0, PChar(E.message), 'Niðurhala skrá', MB_ICONERROR or MB_OK);
        end;
      end;
    finally
      GetData.Free;
    end;
  except
    // you can handle specific exceptions (like file creation errors) or any exception here
  end;
end;

警告 IMHO この設計では、ビジネス ロジック (インターネットからリソース/ファイルを取得してファイルに保存するなど) とユーザー インターフェイス ロジック (エラーが発生した場合にユーザーにメッセージを表示するなど) が混在しています。

一般に、コードは再利用可能であるため、UI ロジックからビジネスを分離するためのより良いアプローチです。

たとえば、次のようにリファクタリングしたい場合があります。

function DownloadToAFile(const sUrl, sFile: string): boolean;
var
  GetData : TFileStream;
begin
  GetData := TFileStream.Create(sFile, fmOpenWrite or fmCreate);
  try        
    IdHTTP.Get(sUrl, GetData);
    Result := (IdHTTP.ResponseCode = 200);
  finally
    GetData.Free;
  end;
end;

function UIDownloadToAFile(const sUrl, sFile: string): boolean;
begin
  try
    Result := DownloadToAFile(sURL, sFile);
  except
    on E: EIDException do //IndyError
      MessageBox(0, PChar(E.message), 'Internet Error', MB_ICONERROR or MB_OK);
    on E: EFileCreateError do //just can't remember the extact class name for this error
      MessageBox(0, PChar(E.message), 'File create Error', MB_ICONERROR or MB_OK);
  end;
end;

procedure SomeOtherCode:
begin
  if UIDownloadToAFile('http://domain.com/file.html', 'c:\folder\file.html') then
    ShowMessage('Got the file')
   else
     ShowMessage('Error !');
end;

明日、サービスまたは DataSnap モジュールを作成している場合は、DownloadToAFile を自由に使用するか、ログまたは Windows イベントにエラーを書き込む新しい ServiceDownloadToAFile を作成するか、HostAdmin に通知する電子メールを送信することができます。それについて。

于 2010-11-11T17:17:59.480 に答える
2

非常に一般的な解決策の 1 つは、「成功」または「失敗」の戻り値を完全に回避することです。関数の代わりに、プロシージャを使用し、代わりに例外を使用して失敗を処理します。

procedure Download(sUrl, sFile: String);

その後

try
  Download ('http://domain.com/file.html', 'c:\folder\file.html');
  ShowMessage('Got the file')
except
  on E:Exxx do 
  begin
    // handle exception
    ShowMessage('Error !');
  end
end;

これには、誰も関数を呼び出すことができず、戻り値を黙って無視するという効果もあります。

于 2010-11-11T18:12:43.753 に答える
1

何らかの理由で、ほとんどの人は、最終的に組み合わせを除いて誤用します。正しい順序は

try 
  // allocate resource here
  try 
  finally
    // free resource here
  end;
except
  // handle exception here
end;

これにより、コンストラクタとデストラクタで例外をキャッチできます。

于 2010-11-11T16:59:21.167 に答える
0

1つだけを使用tryして、これにすべての関数コードを含める必要があります。

于 2010-11-11T16:56:23.497 に答える