Как обрабатывать исключения при создании FileStream
У меня есть такая функция, которую я хотел бы изменить
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 ответов
Просто оберните код, в который вы хотите перехватывать исключения, в блок 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;
Весь смысл обработки исключений состоит из двух частей:
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 file
,download failure
)
Удачи с этим.
--jeroen
Если вы хотите, чтобы ваша функция показывала сообщения пользователю и возвращала ложь при любой ошибке, кодируйте ее следующим образом:
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. об этом.
Одно из популярных решений состоит в том, чтобы полностью избежать возврата "успеха" или "неудачи". Вместо функции используйте процедуру и обрабатывайте ошибки, используя вместо этого исключения:
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;
Это также приводит к тому, что никто не может вызвать функцию и молча игнорировать возвращаемое значение.
По какой-то причине большинство людей злоупотребляют, кроме как, наконец, комбинацией Правильная последовательность
try
// allocate resource here
try
finally
// free resource here
end;
except
// handle exception here
end;
Это позволяет вам ловить исключения в конструкторе и деструкторе.
Вы должны использовать только один try
и получите в этом свой весь код функции.