Wednesday, January 20, 2010

exception handling in .net

If the method does not do what it is supposed to do, it should throw an exception to notify it

Exception handling versus returning error codes

  • When using error code, we must usually find a way to also return whatever the method is supposed to return. That either makes us change the signature and use (out) parameters or (worse) embed the error code and the returned type into another class/structure. 
  • Another reason, and probably most important one, is that whenever we call a method that returns an error code, we must test it otherwise, we may let the code execute in an unstable state and eventually, propagate the error to upper layers of the application. On the other hand, if we throw an exception, it should be either explicitly caught or the application will simply terminate. This makes the code more robust since even if we forget to catch an exception, we will notice it while testing which is not the case if we use error codes and forget to check returned codes.
  • An exception contains much more information than an error code. It contains the exact location of the encountered issue and it should contain, in the message, the exact reason of failure and, for well designed exceptions, how to fix the problem. We no longer need to document each error code and manage error codes duplications. The exception contains all the information developers need to know.
  • If we want an error to propagate to upper callers in the stack, we just let the exception unhandled and it will be propagated to upper layers. Whereas using codes, we must test for the code, return it and do this for all the callers of the method at all levels.
  • Exceptions can be instrumented and monitored using standard applications in Windows.
  • When debugging, we can set the debugger to start when a certain type of exception is thrown. 
There are many “generic” exception types that can be used in different scenarios:
  • ArgumentInvalidException, ArgumentNullException and ArgumentOutOfRangeException
  • InvalidOperationException
If we consider these exceptions, we figure out that they may cover many cases of invalid arguments or, for the latest, an operation that we attempt to execute on an object in an invalid state for instance.
There are other exceptions that we should avoid using such as those thrown by the CLR (OutOfMemoryException, StackOverflowException and alike). Only the CLR should be able to throw these types.

Tester-Doer Pattern
Try-Parse Pattern

The Try Parse pattern consists in exposing a method and an exception safe version of the same method so that other programmers will be able to call it without paying attention to the exception. In .NET framework such pattern is used for the DateTime.Parse method. In fact, there is another version of this method called DateTime.TryParse which returns a boolean indicating whether the method succeeded or not and an OUT parameter that contains the resulting DateTime object in case it is successfull.

We should always avoid a Try-Catch-Finalize block with an empty Catch section. This will only hide potential issues. However, having a catch section that just rethrows the same exception can only be useful if the finalize section does something otherwise, we better remove the try-catch block at all. 

Rethrowing and wrapping

  • When rethrowing the same exception, we better use ‘throw’ (without arguments) otherwise, we will loose stack information.
  • When wrapping an exception, it might be a good idea to include the original exception as the inner exception (to keep some information about the original context).
  • Consider wrapping exceptions to give more details about the context. For instance, a caching component (that does file caching) may have a FileNotFoundException thrown when it tries to access a cache file. However, for its users, that might not have enough information to understand the exception. In this case, we should wrap it in a CacheFileNotFoundException (custom type) that contains information on the query we have done to the caching component for instance. 
When it finds a try block, it then looks for associated catch blocks. If it finds catch blocks, it picks the best one  and executes the code within the catch block. 

When an exception occurs, the proper catch block is determined by matching the type of the exception to the name of the exception mentioned. catch blocks always must be listed from most specific to least specific.
     Reference : click here




    No comments: