Free mag vol1 | Page 337

CHAPTER 7  UNDERSTANDING STRUCTURED EXCEPTION HANDLING system-supplied error dialog box. Typically, you would only rethrow a partial handled exception to a caller that has the ability to handle the incoming exception more gracefully. Notice as well that we are not explicitly rethrowing the CarIsDeadException object, but rather making use of the throw keyword with no argument. We’re not creating a new exception object; we’re just rethrowing the original exception object (with all its original information). Doing so preserves the context of the original target. Inner Exceptions As you might suspect, it is entirely possible to trigger an exception at the time you are handling another exception. For example, assume you are handling a CarIsDeadException within a particular catch scope, and during the process you attempt to record the stack trace to a file on your C: drive named carErrors.txt (you must specify you are using the System.IO namespace to gain access to these I/Ocentric types): catch(CarIsDeadException e) { // Attempt to open a file named carErrors.txt on the C drive. FileStream fs = File.Open(@"C:\carErrors.txt", FileMode.Open); ... } Now, if the specified file is not located on your C: drive, the call to File.Open() results in a FileNotFoundException! Later in this text, you will learn all about the System.IO namespace where you’ll discover how to programmatically determine whether a file exists on the hard drive before attempting to open the file in the first place (thereby avoiding the exception altogether). However, to stay focused on the topic of exceptions, assume the exception has been raised. When you encounter an exception while processing another exception, best practice states that you should record the new exception object as an “inner exception” within a new object of the same type as the initial exception. (That was a mouthful!) The reason you need to allocate a new object of the exception being handled is that the only way to document an inner exception is via a constructor parameter. Consider the following code: catch (CarIsDeadException e) { try { FileStream fs = File.Open(@"C:\carErrors.txt", FileMode.Open); ... } catch (Exception e2) { // Throw an exception that records the new exception, // as well as the message of the first exception. throw new CarIsDeadException(e.Message, e2); } } Notice in this case, we have passed in the FileNotFoundException object as the second parameter to the CarIsDeadException constructor. After we have configured this new object, we throw it up the call stack to the next caller, which in this case would be the Main() method. 275