Exception Handling

From RemObjects Software

Jump to: navigation, search

This is a Oxygene Language Feature topic
Feel free to add your notes to this topic below.


Exception Handling Overview

Exceptions are a standard mechanism in most current structured programming languages that is used to signal error conditions and abort the current flow of execution when errors occur.

Before the introduction of exception handling, errors were usually indicated by returning failure or success codes from method calls, and common code was riddled with checks for these results and if/then clauses that tied further execution on success. What's more, failure to check for results lead to these error conditions simply being ignored and code running on - oblivious tt he fact that a prior method call had failed.

With exceptions, the default behavior when an exception is raised (or thrown, in C++/C# terminology) is that the current flow of execution is interrupted, and the Exception will be propagated up the call stack until it is caught by one or more exception handlers.

Writing Exception Handlers

There are two ways to handle an exception:

  • a try/except block can be used to catch the exception, do optional error handling, and have execution continue as normal from that point on.
  • a try/finally can be used to perform clean-up in case of exception, but have the excpetion continue to bubble up uncaught, afterwards.

Catching Exceptions

In normal day-to-day development, there are very few occasions where user code will actually want to catch exceptions, and application developers should take care to only catch (and handle) very specific exceptions that they except to occur, and let any other "unexpected" exceptions bubble up to the top.

Exceptions may be caught using a try/except code block, which in its simplest form looks like shown here. (again, it is not good practice to blindly catch all exceptions, it is only shown here for illustration ourposes):

try
  // ...code goes here...
except
  Console.WriteLine('There was an exception');
end;

The code inside the except block will only be executed in an exception actually occurred. if the code between try and except finished without problems, execution will skip the except block and resume after the construct.

In either case, execution will resume after the end as if the exception had never happened.

Re-raising Exceptions

The developer can also choose to re-raise the exception, using the raise keyword, in order to provide exception handling but not catch the Exception. This way, the exception will continue to bubble up the call stack.

try
  // ...code goes here...
except
  Console.WriteLine('There was an exception');
  raise; // let the exception bubble up further
end;

Performing Cleanup

Rather then catching exceptions, developers can also use the try/finally block to provide cleanup code that will always be executed - whether an exception occurs or not. This way, the developer can make sure the critical resources are freed or other tasks are performed, and the current method (or section of code) is left in a clean state, even exceptions occur.

var o := new SomeObject
try
  // ...code goes here...
finally
  o.Dispose();
end;

Here, the code inside the finally block will always be executed, even if an exception occurred within the try block. The exception will not be caught, but continue to bubble up the call stack.

The second block always executes, even if an exception has been raised.

Combining except and finally blocks

Oxygene allows you to combine these exception handler types in a single try block, to perform both cleanup and catch the exception:

try
  // ...code goes here...
except
  // ...your exception code...
finally
  // ...your cleanup code...
end;

Exception Selectors

Exception Selectors can be used to limit the scope of a try/except block to one or more exception types. When specified, the exception handler will only catch exceptions of the specified types, and let any other exceptions bubble up.

A second advantage of exception selectors is that by providing an (optional) variable name for the exception, you can also gain access to the exception objects, to inspect its properties (for example to emit the Message property to a log).

try
  // ...code goes here...
except
  on E: SpecialException do
    Console.WriteLine('A special exception occurred. Details: '+E.SpecialDetails);
end;

Multiple exception classes can be listed in the same exception selector, and more than one selctor can be provided within the same except block. If more than one one selector is present, the run-time will look at them in the order of definition, and execute the first selector that matches the current exception. An else clause can also be provided, to handle exceptions that do not match any of the selectors.

If no selector is matched (and no else clause is provided) the exception handler will be skipped, and the exception will bubble up the stack.

try
  // ...code goes here...
except
  on E: SpecialException, CoolException do
    Console.WriteLine('Either a special or a cool exception occurred. Message: '+E.Message);
  on ArgumentException do // optional variable name is omitted
    Console.WriteLine('An AgumentException occurred.');
  else
    Console.WriteLine('Some other exception occurred.');
end;

Each selector (as well as the else clause) can be followed by one statement – including, of course, a begin/end block containing more statements.

When more than one exception type is specified in a single selector, the (optional) variable will be defined as the earliest shared ancestor of all types. In the above example, if both SpecialException and CoolException descended from SharedException, then E would be of that type (i.e. it would not allow access to members specific to SpecialException or CoolException, without casting).

Exception Filters

Exception filters allow you to use more complex logic than just a simple type check provided by the exception selectors described above. By using the where keyword, you can append a boolean statement to the selector, to further qualify the conditions that need to be met for this handler to catch the exception:

try
  // ...code goes here...
except
  on E: SocketException where E.ErrorCode = 10054 do begin
    // connection was closed gracefully
  end;
end;

in the above example, the handler will only execute if the exception is of type SocketException and if it's ErrorCode property equals 10054. In any other case, the exception handler will be skipped and the exception will bubble further up.

Note that this is not the same as providing an if clause within a plain Exception selector. In that case, any SocketException would be caught by the handler, and not only would the developer need to manually determine whether to call raise; to re-raise the exception of the error code did not match, there would also be run-time overhead incurred to catch and re-raise.

More details on exception filters can be found in the Visual Basic .NET documentation (yes, VB.NEt was the first language to expose this exciting .NET feature, and in fact it is still not available in C#, as of now.).

Raising Exceptions

New exceptions can be raised from code manually at any time, using a variation of the raise keyword you have already seen below.

Note: In theory, any object can be raised as an exception, but it is common rule (and actually part of the CLS Standard) to only raise exceptions with types that descend from System.Exception.

Also, while you can use System.Exception itself, it is usually recommended to find (or define) a more suitable sub-class, to describe the exact nature of the exception in more detail. This can be a sub-class provided by the FCL, where suitable, or a class you define yourself. Defining a class yourself also gives you the opportunity to provide additional properties on the exception class, to describe the error condition in more detail.

Using a proper class hierarchy for raised exceptions makes it easier for the consuming code to use exception selectors to catch particular errors.

if MyObject = nil then
  raise new ArgumentNullException('MyObject not initialized');


Product: RemObjects Oxygene (formerly known as Chrome)
Current version: 3.0 Previous Versions: 'Joyride' (2.0), 'Floorshow' (1.5), 'Adrenochrome' (1.0)

GlossaryKeywordsLanguage FeaturesPlatform FeaturesSamplesArticlesHow ToIssues

Personal tools