FURTHER CLARIFICATION: How to correctly write Try.

2019-01-26 01:37发布

RE: How to correctly write Try..Finally..Except statements?

I'm still confused by the OP's original question. Specifically, the last line of the procedure (outside of the try..finally..end) that reads "Screen.Cursor:=crDefault".

My understanding is that any exceptions raised inside a try..except|finally..end block WILL execute the code after the "end" of the "try".

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;

In the above example, I don't see any reason why "Screen.Cursor:=crDefault" would not be executed. Please correct me if I'm wrong.

As a further example, I've compiled this little bit of code to help illustrate. When the code is ran, THREE (3) ShowMessage() dialogs will be presented. The first "Exception Raised" and the second "finally" and the third "at end".

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;

So, I'm confused on why his "Screen.Cursor:=crDefault" isn't being ran, in it's original form and code. Can someone please elaborate?

3条回答
不美不萌又怎样
2楼-- · 2019-01-26 02:01

The code you posted seems to work fine, because you're able to handle all possibilities. Try changing it somewhat though, so that there's an exception raised your code doesn't handle:

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;

Run this, and you'll see Finally, then the exception for 42, but no Got here message. This is because the exception took you out of the current block, the stack is unwound, and the code from the end of the finally to the end of the procedure never executes.

Move the final ShowMessage call from where it is to inside the finally and run again.

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;

You'll now see both calls to ShowMessage in the finally block, one after the other, but not the one after the finally block's end;. Code inside the finally block is guaranteed to execute, while code beyond it may or may not.

To make it even more clear, the presence of the try..except block can be removed:

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

The entire purpose of the try..finally block is to ensure that code inside the finally section will execute before the procedure ends.

查看更多
做自己的国王
3楼-- · 2019-01-26 02:03

You don't actually catch the exception. In this case, upon exception, the "finally" block of code will execute, and then the exception will unwind the stack.

查看更多
做个烂人
4楼-- · 2019-01-26 02:09

In Delphi, the finally block doesn't really handle the exception that occured in the try block. It only guarantees that the code in the finally block will always be executed, whether an exception occured or not in the try block. If an exception really happened in there, it won't get caught. And when an exception didn't get caught, you know what happened to the code below it.

To catch the exception that might be occured, use the try...except... block instead. You can combine these two construct to do those two action: (1) guarantee the execution some piece of code, and (2) catch the exceptions which might occured. The common usage is like this:

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;

So, you should change your code and move the Screen.Cursor := crDefault; inside the finally block. In addition, add the try...except... block to surround the try...finally... block. Like this:

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;

Or, if you are not sure that the code Obj := TSomeObject.Create; is safe enough, you should add the second try...finally... block to surround it, like this:

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;

There, hope it helps :)

查看更多
登录 后发表回答