2010-11-11 6 views
3

У меня есть функция, как это, что я хотел бы реорганизоватьКак обрабатывать исключения при создании 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 выше.

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

+1

И вопрос в том, что? – jachguate

+0

Вопрос был в комментарии коментария – Sigurdur

ответ

9

Просто перенесите код, где вы хотите, чтобы исключение ловушки в 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; 
+0

Эта конструкция ближе всего к тому, что я искал. – Sigurdur

0

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

0

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

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

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

+6

Я не согласен с вами ... есть случаи, когда вы хотите обрабатывать исключения только в том случае, если конструктор преуспевает, но вы хотите, чтобы исключение выполнялось, если сам конструктор терпит неудачу. Я думаю, что нет никакого общего рецепта для заказа или гнезда try/finally или try/except blocks, ** всегда зависит ** от того, что вы хотите. – jachguate

+0

@jachguate Нет, это не так. В последовательности except-finally вы никогда не ломаете ошибки в коде очистки. Кроме того, если улов ловит все, в конечном итоге просто бесполезно. –

+0

@Eugene: вы можете обрабатывать любые исключения в предложении except, но иногда вы обрабатываете некоторые определенные исключения, иногда вы повторно поднимаете исключения ... так что предложение finally не бесполезно _ по определению_ после предложения except. – jachguate

1

Если вы хотите, чтобы ваша функция, чтобы сообщения для пользователя и вернуть ложна на любой неисправности, код его следующим образом:

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

jachguate у вас есть очень действительная точка относительно разделения пользовательского интерфейса от логики бизнеса – Sigurdur

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; 

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

+0

+1 для объяснения того, как вы должны делать исключения правильно. –

3

Все дело об обработке исключений в два раза:

  • 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 обрабатывается
  • все неудачи (cannot create file, download failure)

Удачи вам в этом.

--jeroen

+0

Я согласен с этой конструкцией как более разумной в целом. Однако была причина, по которой я хочу, чтобы окно сообщения находилось внутри функции «Загрузить». – Sigurdur

+0

Вы всегда можете пересмотреть, почему вы смешиваете код пользовательского интерфейса в бизнес-логике ;-) –