Exceptions and Error Codes

I see Miguel has some
comments on exceptions
that I must disagree with… "urn:schemas-microsoft-com:office:office" />

The beauty of
returning a pointer to an object, is that you can return NULL as your standard
error return value, and if the value mattered, you will most likely take the
necessary steps to check its return value, or they application will just crash
the moment you try to use it.

This could be
applied to the .NET and Java APIs to reduce the amount of required try/catchs.
For example, the File.IO.OpenRead method could be (shown here is the artist's
representation):

   public static FileStream OpenRead (string file)
   {
          int fd = open (file);
          
          if (f == -1)
                  return null;
          return new FileStreamFromFD (f);
   }

Which makes null a
valid return value. If you fail to check for the return value, you would get a
nice NullReferenceException, but you would allow the expensive creation of an
exception object, and the ugly try/catch block in the call site.

Obviously we can not
change the existing APIs, but we could encourage developers to minimize their
use of exceptions. In my limited personal experience with software design, when
I have faced applications with tons of Exceptions it was extremely hard to keep
up with them. A rewrite of the software to avoid exceptions and use the more
sane API paid for.

Here is the rationale for exceptions
rather than error codes for exceptional
situations. (thanks to Jason Clark for writting these up for
us)

There are many benefits to exception handling as compared to return
value based error reporting. In fact, the Framework exposes more of the benefits
of exception handling then some other common exception models such as Win32 SEH
or C++ exception handling. Good managed API design does not restrict the
application developer from realizing the benefits of exceptions. The following
are some of these benefits.

Exceptions Promote API Consistency

Consistency of API is important to developers. Exceptions
promote consistency, because they are designed to be used for failure reporting
(and nothing else). In contrast, return values have many uses of which
failure reporting is only a subset. For this reason, it is likely that
APIs that report failure through return values will find a number of patterns,
while exceptions can be constrained to specific patterns. The Win32 API is
a clear example of this inconsistency through BOOLs, HRESULTS, and
GetLastError() among others.

Exceptions are Compatible with Object Oriented
Features

Object-Oriented languages tend to impose constraints on method
signatures that are not imposed by functions in functional programming.
For example, constructor methods, operator overloads, virtual methods, generic
methods, interface methods and properties all resolve to functions, and yet the
developer writing the method has no choice in the return value (if any) of the
method. For this reason, it is not possible to standardize on return value
based error reporting for object oriented APIs. An error reporting method, such
as exceptions, which is out of band of the method signature is the only
option.

With Exceptions,
Error Handling Code Need not be Near Failing Code

With return-value based error reporting error handling code is
always very near to the code that could fail. However, with exception
handling the application developer has a choice. Code can be written to
catch exceptions near the failure point, or the handling code can be further up
in the call stack.

With Exceptions, Error Handling Code is More
Localized

Very robust code that reports failure through return values tends
to have an if statement for every functional line of code. The job of
these if statements is to handle the failure case. With exception oriented
code, robust code can often be written such that a number of methods and
operations can be performed with the error handling logic grouped at the end of
the try block (or even higher in the call-stack).

A common argument is that
exception handling code is unsightly. This argument is usually being made
by someone comparing well written exception handling code with return value
oriented code that is not sufficiently checking return values of
functions. 

Exceptions Are
Not Easily Ignored

Return values can be easily ignored, and often are. Exceptions, on
the other hand, take an active role in the flow of your code. This makes
failures reported as exceptions difficult to ignore, and improves the robustness
of code.
For example the Win32 API CloseHandle() fails very rarely, and so it
is common (and appropriate) for many applications to ignore the return value of
the API. However, if any application has a bug which causes CloseHandle()
to be called twice on the same handle, it would not be obvious unless the code
was written to test the return value. A managed equivalent to this API
would throw an exception in this case and the unhandled exception handler would
log the failure and the bug would be found.

Exceptions Allow for Unhandled Exception
Handlers

Ideally, every application is written to intelligently handle all
forms of failure. However, this is not realistic as all forms of failure
can’t be known. With return value oriented code, failures that are
unexpected are ignored by code and the application continues to run with
undefined results. With well written exception based code, unexpected
failures eventually cause an unhandled exception handler to be called.
This handler can be designed to log the failure, and can also make the choice to
shut down the application. This is far preferable to running with
indeterminate results, and also provides for logging that makes it possible to
add more significant error handling for the previously unexpected case.
(Today, Microsoft Office uses an unhandled exception handler to gracefully
recover and re-launch the application as well as send error information to
Microsoft to improve the product.)

You should
only have a custom unhandled exception handler, if you have a have specific,
app-oriented work to do in the handler. If you just want error reporting to
occur, the runtime will handle that automatically for you and you do not need to
(and should not) use a UEF. An application should only register a UEF if it can
provide functionality above and beyond the standard error reporting that comes
with the system and runtime.

Exceptions Provide Robust Error Info for
Logging

Exceptions provide for a consistent error handling mechanism, and
as such make it possible to have consistent logging of error information.
The System.Exception type in the Framework encapsulates stack-frame information
as well as other useful information such as a nested exception type. This
is great for error logging and discovery of unexpected error cases during the QA
phase. Exceptions are objects, so it is possible to extend exceptions to
add additional information.

Handling Errors
Consistently and Thoroughly are Essential for Creating a Good User
Experience

The application developer needs to provide specific information
about what error occurred, why it occurred, and what can be done to mitigate it.
This requires detailed and specific error returns from API calls. The exception
mechanism allows detailed error information to be created by the API developer.
There is no need for the API developer to change global header files to add new
error codes. The API developer can customize the exception subclass as much as
necessary to transmit all of the details of the error to the
caller.

Exceptions Promote
Instrumentation

Exceptions are a well-defined method-failure model. Because
of this, it is possible for tools such as debuggers, profilers, performance
counters, and others to be intimately aware of exceptions. The PerfMon
application, for example, keeps track of exception statistics. In the
future automatic instrumentation from profiling to auto-documentation will be
more sophisticated related to exceptions. Methods that return failure do
not share in the benefits of instrumentation.

Exceptions Unify
the Error Model (Hard and Logical Errors)

Exception handling or interrupts are the only option for hard
errors such as null references and divisions by zero (certain operations have no
return-value means of reporting failure). Using exception handling for API
and logical failure reporting is beneficial in that it unifies error handling
for all failure types.