I see Miguel has some comments on exceptions that I must disagree with…

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.