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

关于异常处理的重点有两点:

  • finally用于资源清理;您经常在业务逻辑中看到这一点
  • except用于对特定异常做出反应(并通过函数结果和中间变量摆脱状态逻辑);你在业务逻辑中几乎看不到它

在你的情况下:

Myfunction不应该返回布尔值,不包含except块,不执行 aMessageBox而只是让异常传播。
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处理您的一个地方
  • 所有的失败都被平等地处理 ( cannot create file, download failure)

祝你好运。

——杰伦

于 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;

警告 恕我直言,这种设计混合了业务逻辑(例如从 Internet 获取资源/文件并将其保存到文件中)和用户界面逻辑(例如在出现错误时向用户显示消息)。

一般来说,将业务与 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 或编写新的 ServiceDownloadToAFile ,然后将错误写入日志或 Windows 事件,或者发送电子邮件通知 HostAdmin关于它。

于 2010-11-11T17:17:59.480 回答
2

一种非常流行的解决方案是完全避免“成功”或“失败”返回值。代替函数,使用过程并使用异常处理失败:

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

出于某种原因,大多数人误用了 except-finally 组合。正确的顺序是

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

这使您可以在构造函数和析构函数中捕获异常。

于 2010-11-11T16:59:21.167 回答
0

您应该只使用一个try并将所有功能代码放入其中。

于 2010-11-11T16:56:23.497 回答