ДАЛЬНЕЙШЕЕ УТОЧНЕНИЕ: Как правильно писать операторы Try..Finally..Except?

RE: Как правильно писать операторы Try..Finally..Except? < / а>

Меня все еще смущает исходный вопрос OP. В частности, последняя строка процедуры (за пределами try..finally..end), которая читает «Screen.Cursor: = crDefault».

Насколько я понимаю, любые исключения, возникающие внутри блока try..except | finally..end, БУДУТ выполнять код после «конца» «попытки».

procedure TForm1.Button1Click(Sender: TObject);
var
  Obj: TSomeObject;
begin
  Screen.Cursor := crHourGlass;

  Obj := TSomeObject.Create;
  try
    // do something
  finally
    Obj.Free;
  end;
  Screen.Cursor := crDefault;
end;

В приведенном выше примере я не вижу причин, по которым "Screen.Cursor: = crDefault" не будет выполняться. Пожалуйста, поправьте меня, если я ошибаюсь.

В качестве еще одного примера я скомпилировал этот небольшой фрагмент кода, чтобы проиллюстрировать это. При запуске кода будут представлены ТРИ (3) диалога ShowMessage (). Первое «исключение поднято», второе «наконец» и третье «в конце».

procedure TForm1.Button1Click(Sender: TObject);
begin
   try
      try
         showMessage(format('%s', [12]));
      except
         showMessage('Exception raised');
      end;
   finally
      showMessage('finally');
   end;
   showMessage('at end');
end;

Итак, я не понимаю, почему его "Screen.Cursor: = crDefault" не запускается в исходной форме и коде. Кто-нибудь может уточнить?


person Vin Colgin    schedule 21.11.2013    source источник


Ответы (3)


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

procedure TForm1.Button1Click(Sender: TObject);
begin
  try
    try
      raise Exception.Create('42');
    except
      on E: EDivByZero do
        ShowMessage('DivByZero');
    end;
  finally
    ShowMessage('Finally');
  end;
  ShowMessage('Got here');
end;

Запустите это, и вы увидите Finally, затем исключение для 42, но нет сообщения Got here. Это связано с тем, что исключение вывело вас из текущего блока, стек разматывается, а код от end до конца процедуры никогда не выполняется.

Переместите последний вызов ShowMessage с того места, где он находится, внутрь finally и запустите снова.

procedure TForm1.Button1Click(Sender: TObject);
begin
  try
    try
      raise Exception.Create('42');
    except
      on E: EDivByZero do
        ShowMessage('DivByZero');
    end;
  finally
    ShowMessage('Finally');
    ShowMessage('Got here');
  end;
  ShowMessage('Will never get here');
end;

Теперь вы увидите оба вызова ShowMessage в блоке finally, один за другим, но не вызовы после end; блока finally. Код внутри блока finally будет выполняться гарантированно, а код за его пределами может или не может.

Чтобы было еще понятнее, наличие блока try..except можно убрать:

procedure TForm1.Button1Click(Sender: TObject);
begin
  try
    raise Exception.Create('42');
  finally
    ShowMessage('Finally');
    ShowMessage('Got here');
  end;
  ShowMessage('Will never get here');
end;

Вся цель блока try..finally - обеспечить выполнение кода внутри раздела finally до завершения процедуры.

person Ken White    schedule 21.11.2013
comment
Думаю, я лучше понимаю твой пример. По сути, если в except есть предложение on и если Exception не обрабатывается предложением on, тогда случаются плохие вещи и код в процедуре после try ... end не выполняется. Итак, в любое время, когда используется try..except..end И добавляется предложение on для перехвата определенных исключений; эти блоки кода необходимо проверить, чтобы гарантировать, что любые исключения NOT, перехваченные исключением ... on, обрабатываются правильно. - person Vin Colgin; 22.11.2013
comment
@VinK: Не совсем так. Если пункта except нет вообще, могут случиться плохие вещи и вызвать ту же проблему. Удалите try..except..end полностью, но оставьте оператор raise; вы все равно увидите, что код между концом finally и концом процедуры не выполняется. Вся цель блока finally - убедиться, что код выполняется. Код после него может не работать. - person Ken White; 22.11.2013
comment
Вас понял. Спасибо за разъяснение этого поведения. - person Vin Colgin; 22.11.2013
comment
@ Джерри: Спасибо. Я признаю, что это было запоздалой мыслью; Я изменил его с 12 плаката во время редактирования. :-) Я надеюсь, что это не была настоящая причина голосования за. :-( - person Ken White; 22.11.2013
comment
Отличный ответ, просто саркастический - person Jerry Dodge; 22.11.2013

В Delphi блок finally на самом деле не обрабатывает исключение, которое произошло в блоке try. Это только гарантирует, что код в блоке finally всегда будет выполняться, независимо от того, произошло ли исключение в блоке try. Если там действительно произошло исключение, его не поймают. А когда исключение не было обнаружено, вы знаете, что случилось с кодом под ним.

Чтобы перехватить исключение, которое могло произойти, используйте вместо этого блок try...except.... Вы можете объединить эти две конструкции для выполнения этих двух действий: (1) гарантировать выполнение некоторого фрагмента кода и (2) перехватить исключения, которые могут возникнуть. Обычное использование выглядит так:

try
  try
    // do something that might cause an exception.
  finally
    // do something that must be executed WHATEVER happened.
  end;
except
  // do something ONLY IF an exception has occured.
end;

Итак, вам следует изменить свой код и переместить Screen.Cursor := crDefault; внутрь блока finally. Кроме того, добавьте блок try...except..., чтобы окружить блок try...finally.... Нравится:

procedure TForm1.Button1Click(Sender: TObject);
var
  Obj: TSomeObject;
begin
  Screen.Cursor := crHourGlass;
  Obj := TSomeObject.Create;
  try
    try
      // do something.
    finally
      Obj.Free;
      Screen.Cursor := crDefault;
    end;
  except
    ShowMessage('An error has occured!');
  end;
end;

Или, если вы не уверены, что код Obj := TSomeObject.Create; достаточно безопасен, вы должны добавить второй блок try...finally..., чтобы окружить его, например:

procedure TForm1.Button1Click(Sender: TObject);
var
  Obj: TSomeObject;
begin
  Screen.Cursor := crHourGlass;
  try
    try
      Obj := TSomeObject.Create;
      try
        // do something.
      finally
        Obj.Free;
      end;
    finally
      Screen.Cursor := crDefault;
    end;
  except
    ShowMessage('An error has occured!');
  end;
end;

Надеюсь, это поможет :)

person Choerun Asnawi    schedule 22.11.2013

На самом деле вы не поймаете исключение. В этом случае при возникновении исключения будет выполнен блок кода «finally», а затем исключение раскрутит стек.

person DarkWanderer    schedule 21.11.2013
comment
Я не уверен, что ты имеешь в виду. Исключение перехвачено и сообщается, что возникло исключение, затем сообщается «Наконец», а затем - «в конце». Все 3 диалоговых окна ShowMessage () будут показаны в результате попытки выполнить format (), который сгенерирует исключение. Короче говоря, последняя операция в процедуре выполняется в обоих примерах. Итак, я не понимаю, почему последняя строка в процедуре должна быть включена в try..inally..end - person Vin Colgin; 22.11.2013
comment
У вас есть выражение except во втором фрагменте кода, но его нет в исходном коде. кроме того, что ловит исключение, а не окончательно - person DarkWanderer; 22.11.2013