Domanda

Generally I make my exception handlers handle only very specific exceptions, so that the code doesn't try to recover from exceptions that weren't anticipated and potentially aren't recoverable.

But there are a few places where it is impossible to know ahead of time what exceptions could legitimately come up from which recovery is perfectly possible (because of calls to libraries that don't specify what exceptions they might raise). So in these special cases I do in fact trap all exceptions: on e:Exception ....

But this also handles EAssertionFailed exceptions, which really should never be handled because they imply incorrect code.

So I've started writing exception handlers like this:

on e:Exception do
begin
  if e is EAssertionFailed then
  begin
    raise;
  end;

  …

This just seems ugly and error-prone. (What if I forget the if e is EAssertionFailed code?)

Is there a better way? (Should I just never ever use on e:Exception …, except in a top level handler that aborts the program?)

Update

I think David's comment below is correct - never use a blanket exception handler (other than to log the exception and stop the program).

The problem then becomes "How can I get a list of all exception types that are safe to handle arising from some arbitrary bit of code?" I don't think this is one that Stack Overflow can answer.

(The particular problem is a 3rd party library that reads a configuration file. There are fairly obvious exceptions for filing system errors, but the library seems to raise numerous and almost random exception types for syntax errors within the file itself. I don't think I could ever be sure that I've exhausted the possibilities. I certainly cannot have my application fall over because the configuration file couldn't be read!)

È stato utile?

Soluzione 3

I'm offering my own answer here to see what the community thinks.

The point wasn't a misunderstanding of how exception handling works in Delphi. I've summarised the particular problem in my update to the question.

My solution is going to be to convert the many different exception types the library can raise into a single type that I can handle explicitly. I'll put a blanket exception handler around the one problem call to the library. This handler will raise a new exception of a known type, to be caught explicitly higher up at the appropriate time.

(And no, the exceptions raised by the library call do not have any common base type other than Exception itself, so I really do have to catch all exceptions if I want to recover from a failure in the library call.)

Crucially, I believe that although I cannot hope to enumerate all the exceptions that this library call could raise, I can be sure that it won't put the application into an unstable state when it raises an exception.

Update

In the end I was able to use something like this:

on e:Exception do
begin
  if (e.ClassType = Exception) or (e is EConvertError) or
     (e is EVariantError) then
  begin
    ShowMessage('The application settings could not be loaded.');
  end
  else
  begin
    // An exception we didn't anticipate. Assume it is fatal.
    raise;
  end;
end;

A lot of the recoverable exceptions raised by the library are of type Exception, but note that only Exception and the specified descendants are handled. The others are raised indirectly by the runtime library. There might turn out to be other exceptions that should be handled too, but this seems like a good start. It's better to have the application bail out when it needn't have done so than to continue running and corrupt the user's data.

Altri suggerimenti

The standard Delphi exception handling pattern is

  try
// Some code which may raise an exception
  except
    on E: SomeError do begin
      // Handle SomeError and derived classes
    end;
    on E: SomeOtherError do begin
      // Handle SomeOtherError and derived classes
    end;
    ...
  end;

it does not require the use of on E:Exception ….

If you don't know the exception type you should not handle it; or sometimes (ex in DLL code) you should handle ALL exceptions.

Why don't to use this:

try
  ...
except
  on EAssertionFailed do raise;
  on E: .... do ... ; // your exception
  on E: .... do ... ; // your exception
  on E: .... do ... ; // your exception
  on E: Exception do ... ; // all other exceptions
end;

If any of the handlers in the exception block matches the exception, control passes to the first such handler. An exception handler 'matches' an exception just in case the type in the handler is the class of the exception or an ancestor of that class.

Or you can remove on EAssertionFailed do raise; and on E: Exception do ... ; from the exception block and then exception handler will be searched in the next-most-recently entered try...except statement that has not yet exited.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top