ДОПОЛНИТЕЛЬНАЯ РАЗЪЯСНЕНИЕ: Как правильно написать операторы Try..Finally..Except?

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

Я все еще смущен оригинальным вопросом ОП. В частности, последняя строка процедуры (за пределами 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" не запускается, в его оригинальной форме и коде. Может кто-нибудь уточнить, пожалуйста?

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 блок, один за другим, но не один после finally блока end;, Код внутри 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 раздел будет выполнен до завершения процедуры.

В Дельфи 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;

Там, надеюсь, это помогает:)

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

Другие вопросы по тегам