Как обрабатывать исключения при создании FileStream - PullRequest
3 голосов
/ 11 ноября 2010

У меня есть такая функция, которую я хотел бы рефакторировать

   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 выше.

С наилучшими пожеланиями

Ответы [ 6 ]

9 голосов
/ 11 ноября 2010

Просто оберните код, в который вы хотите перехватывать исключения, в блок 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;
3 голосов
/ 11 ноября 2010

Весь смысл обработки исключений двоякий:

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

Теперь код многоуборщик:

  • больше нет пользовательского интерфейса в вашей бизнес-логике
  • одно место, где обрабатывается except
  • все ошибки обрабатываются одинаково (cannot create filedownload failure)

Удачи с этим.

- jeroen

2 голосов
/ 11 ноября 2010

Одно из популярных решений - полностью избежать возвращаемых значений «success» или «fail». Вместо функции используйте процедуру и обрабатывайте ошибки, используя вместо этого исключения:

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;

Это также приводит к тому, что никто не может вызвать функцию и молча игнорировать возвращаемое значение.

1 голос
/ 11 ноября 2010

Если вы хотите, чтобы ваша функция показывала сообщения пользователю и возвращала 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;

Внимание ИМХО, этот дизайн смешивает бизнес-логику (такую ​​как получение ресурса / файла из Интернета и сохранение его в файл) и логику пользовательского интерфейса (например, отображение сообщений пользователю в случае ошибок).

В целом, это лучший подход для отделения бизнеса от логики пользовательского интерфейса, поскольку ваш код можно использовать повторно.

Например, вы можете изменить коэффициент следующим образом:

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.

0 голосов
/ 11 ноября 2010

По какой-то причине большинство людей злоупотребляют, кроме как, наконец, комбинацией.Правильная последовательность:

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

Это позволяет вам ловить исключения в конструкторе и деструкторе.

0 голосов
/ 11 ноября 2010

Вы должны использовать только один try и получить в этом свой код всех функций.

...