Click here to monitor SSC
  • Av rating:
  • Total votes: 13
  • Total comments: 7
Steven R. McCabe

Taking Exceptions

19 January 2008

Exceptions End to End

Steve looks at the requirements that are usually in force for the support of a team-based 'production System' and suggests a way that exception-handling in C# can be used to provide robust error reporting

I'm going to describe, and provide, a simple framework for a common and systematic approach to the use of exceptions. There are many ways of skinning a cat in Software Development  This article contains just one man's view. It is a view resulting from twenty plus years in the field, untold number of projects and interaction with 100 plus developers, but it is still just a personal view. With that being said, “Take and use what you might find of value, and throw away that which has no value.”

The source for this article is available at http://www.cabercomputing.com/Downloads/ExceptionsEndToEnd.zip, or in the speech bubble at the head of the article.

Who:

The class library, business layer developer has a responsibility to other developers who will use their APIs. Typically, good class library designers do a fair job of documenting their APIs but do a miserable job of documenting the exceptions which could be thrown during the execution of their library.

Additionally, little thought is given for the person who will have to suffer through the task of maintaining the class library.

What:

You will need to be concerned with the following points, when developing a common system approach for exceptions.

  1. All of your exceptions should inherit from one common exception class.
  2. A central point is needed where all exceptions are to pass through before throwing. This is useful to provide:
    • A single point for setting one breakpoint before any exception is actually thrown.
    • One spot to log and/or forward the exceptions via email.
  3. A unique identification is needed for each and every exception which could be thrown. A defined, discrete list of all possible exceptions relative to the class being used by the caller is needed.
  4. A message is needed that can be defined independently from the exception. Seldom is an application developer capable of defining a message which is appropriate for the end user.
  5. A loosely coupled approach is needed to allow for parameter substitution in the message.
  6. An approach which can support internationalization (globalization) of messages.
  7. Explicit visual clues for the maintenance programmer as to the requirements for successful execution of each and every method in a class.
  8. Critical classes, like entry points to a tier, require special handling. Typically this is done so as not to expose private details to the caller, like a connection string to a database.

Let’s take a look at three classes to pull this off.

First of the three classes is a base class which all of your future exception classes should inherit from:

namespace CaberComputing.Utilities

{

       public class ExceptionX : System.ApplicationException

       {

              public enum Error

              {

                     InternalInvalidValue_For_,

                     InternalArgument_IsNull,

                     InternalUnknownErrorOccurred,  // Unknown Catch all

              }

 

              private System.Enum _ErrorType;

 

              public ExceptionX( System.Enum       aErrorType,

    string            aMessage,

    System.Exception  aInnerException )

                     : base(aMessage, aInnerException)

              {

                     _ErrorType = aErrorType;

              }

 

              public System.Enum  ErrorType

              {

                     get

                     {

                           return _ErrorType;

                     }

              }

 

              public bool IsA( System.Enum  aErrorType )

              {

                     bool isA = _ErrorType.Equals(aErrorType);

 

                     return isA;

              }

       }

}

 

The ExceptionX provides the fundamental piece missing from System.Exception, and that is a unique identifier..

Within a single business layer, we might have 100-1000 different cases which need to be thrown as an exception. The caller needs to distinguish one case from another. Obviously we are not going to define 100 to 1000 different exception classes; therefore we need to properly distinguish one case from another. The property “ErrorType” does just that!

It is better for the caller if you create one exception class, which derived from ExceptionX, for each of the classes in your library. This allows the caller to catch any and all exceptions forcefully thrown by your class with one single catch statement.


Bertrand Meyer, creator of one of the first object oriented languages, “Eiffel”, had an interesting concept which was built into that language. A developer of a method in a class could:

  1. Define pre-condition(s) which must be met(true) before the body of the method was executed,
  2. Define post-condition(s) which must be true after the body of the method has executed, and tested by the language before successfully returning to the caller.
  3. Define zero or more invariants which are always to be true, or the language would throw an exception.

Since the C# language does not support this concept out of the box, the second of the three classes, “AssertX” provides a somewhat similar functionality.

 

public sealed class AssertX

{

       static public void PreCond( Enum  aError, bool  aCondition, params object[]  aObjects)

       {

              if (!aCondition)

              {

                     Throw(aError, aObjects);

              }

       }

       static public void PostCond( Enum  aError, bool  aCondition, params object[]  aObjects )

       {

              if (!aCondition)

              {

                     Throw(aError, aObjects);

              }

       }

 

       static public void Invariant( Enum  aError, bool  aCondition, params object[]  aObjects )

       {

              if (!aCondition)

              {

                     Throw(aError, aObjects);

              }

       }

 

       static public void Throw( Enum  aError, params object[]  aObjects )

       {

              Throw(aError, null as System.Exception, aObjects);

       }

 

       static public void Throw( Enum             aError,

                                 Exception        aInnerException,

                                 params object[]  aObjects )

       {

              ExceptionX  exception = Resource.Build( aError.GetType(),

                                                      aInnerException,

                                                      aError,

                                                      aObjects);

 

              // TODO: Convenient spot for set a breakpoint, Logging, and/or email

 

              throw exception;

       }

 

private  AssertX() {}     // Prevents construction of an instance of this class

}


 

 

One thing to note: the Throw method of the AssertX class calls Resource.Build(…).  This brings us the last required class in the framework.  The last class is called “Resource”, and it performs the magic of building the exception’s message and creating the actual exception to be thrown (More on this later). 

The actual implementation of the Resource class is beyond the scope of this article.

The Resource class will build a default message for the Error Enum. 

Example #1:

AssertX .Throw(Error .InternalUnknownErrorOccurred);         becomes

“Internal unknown error occurred.”

Example #2:

AssertX .Throw(Error .InternalArgument_Null, “aMsg”);        becomes

“Internal argument(aMsg) is null.”

Example #3:

int count = 32;

AssertX .Throw(Error . InternalInvalidValue_For_, count, “count”);         becomes

 “Internal invalid value(32) for(count).”

Additionally, the Resource class will look for a resource entry for Error Enum in a resource file named “ResouceExceptions” within the assembly in which the Error Enum is defined. 

Example #1:

ExceptionX . Error . InternalUnknownErrorOccurred     resource name is

“ExceptionX__InternalUnknownErrorOccurred”            resource value of

“An internal error has occurred!  Unknown error.”

Example #2:

ExceptionX . Error . InternalArgument_Null            resource name is

“ExceptionX__InternalArgument_IsNull”                 resource value of

An internal error has occurred!  Argument: {0} was null and that is not allowed.

Example #3:

ExceptionX . Error . InternalInvalidValue_For_        resource name is

“ExceptionX__InternalInvalidValue_For_”               resource value of

An internal error has occurred!  An invalid value of {0} was supplied for {11}.

Remember, resource entries are optional. This allows the development process to move along efficiently, since at any future point in time any message for any error can be changed, without affecting the code base.

Where:

Where does a user of your class find the possible errors/exceptions that might be thrown?

Every class should contain a master list of all possible exceptions that can be thrown during its execution. The designer of the class must clearly document the exceptions which can be thrown for each method and/or property using only those items contained in the class’s master list.

Each item in the class’s master list must:

  1. Be unique to the list.
  2. Should be descriptive and not cryptic.

    Example:

               public enum Error

               {

                      User_AlreadyExist,

                      User_Age_Invalid,

                      InternalInvalidUserId_

                      InternalInvalidArgument_

               }

The master list is to be contained as an element of the class which this list is for. My convention is for the master list to be contained in one single enum which is always named Error. The developers who are using your class will quickly and easily be able to see the complete list of items via IntelliSense on “{YourClassName}. Error“. Here is a little sample class demonstrating the approach.

   public class User

   {

        public enum Error             // Master List

        {

              User_AlreadyExist,

              User_Age_Invalid,

              InternalInvalidUserId_,

              InternalInvalidArgument_

       }

 

       public class ExceptionX : CaberComputing.Utilities.ExceptionX

       {

              public ExceptionX( System.Enum       aErrorType,

        string            aMessage,

        System.Exception  aInnerException )

                        : base(aErrorType, aMessage, aInnerException)

              {

              }

       }

 

      // Member(s) …

 

      // Constructor(s) …

 

      // Properties(s) …

 

      public void  Record(string aMsg)

      {

          Assert.PreCond(User.Error.InternalInvalidArgument_, aMsg != null, “aMsg”);

 

   // Your Implementation goes here …

      }

  }

As a developer of a class, you have an obligation to the caller of your class to provide quality and resilent code.

Three approaches can be taken when an exception occurs in the middle of the execution of your code.

Approach 1: Ignore an exception – Let the caller deal with it

This is the simplest approach, and the one most often taken by new and non-seasoned developers. It requires almost no thought but, unfortunately, it generally results in heartache for the developer who must call your code. Since the class library’s designer has not identified the exceptions, it is left to the caller of your code to determine what exception(s) might occur during the execution of the code.

 

This approach is discouraged unless you are :

  1. Willing to document all possible exceptions and
  2. Very confident that your class will not be left in an unpredictable/inconsistent state, which might affect future calls.

Approach 2: Try-Catch / Re-tag

This is a good approach. It requires you to wrap a block of your code in a Try-Catch block. In the catch section of your code, it is high suggested that you "re-tag" the exception you have caught to one of your nested exceptions, of type "ExceptionX". While the re-tagging is optional, it eases the work incurred by your caller. If you choose not to re-tag, then you must document the fact that the lower-level exception can be thrown. Additionally, the catch portion of the Try-Catch block affords you the opportunity to restore your object to a known and reliable state, so that it can service future calls correctly.

 

NOTE: There is a performance hit taken every time an exception is thrown and caught. While generally this is not significant, it can become so when the caller has a tight loop that calls your code to determine the percent of acceptable cases.

public void Record(string aMsg)         // Approach #2

    {

        try

        {

            string msg = aMsg.ToString();

 

            // Your Implementation goes here …

        }

        catch (System.ArgumentNullException aException)

        {

            // Re-Tag

            AssertX.Throw(User.Error.InternalArgument_IsNull, aException, aMsg != null, "aMsg");

        }

    }

  

Approach 3: Prevent lower level exceptions from occurring

Where possible prevent lower level exceptions from occurring. Try to verify as many things as possible before changing any state in your object. This help minimize your effort to restore the object to a consistent and known state before returning to the caller. This approach when used with the framework layout in this article, results in few lines of code then the try-catch and re-tag approach.

     public void Record(string aMsg)         // Approach #3

     {

         AssertX.PreCond(User.Error.InternalArgument_IsNull, aMsg != null, "aMsg");

 

         // Your Implementation goes here …

     }

No one single approach is the right for every case. It is up to the developer to choose the right approach for the task at hand. It is strongly suggested to try to use Approach 3 first and then when necessary employ Approach 2, and seldom if ever use Approach 1.

Why:

Why do some of the items in the Error enum contain underscores? Each underscore is used to represent where a parameter substitution is planned to occur at runtime when the exception is actually thrown.

Why do some of the items in the Error enum start with the word “Internal”? As a developer of this class, I realized those items which are truly not an end user error. This area can be a little gray sometimes, but give it your best shot. Let’s take the item, “InternalInvalidArgument_”, it would be used when argument passed to a method must not be null.

Why use an enum? They are:

  1. Cheap (in terms of memory),
  2. Convenient, easy to use for both the class developer and caller (intellisense)
  3. Provide strong type checking
  4. Automatically numbered and
  5. Provide the unique identity

I have seen people use the Message property of the Exception class to try distinguish one error from another. This approach is brittle and prone to errors, since changes to the original message text requires subsequent client code changes. The purpose of the Message should be for the end user and note the developer of your class. Just as your class provides a contract to the caller for its interface, the caller needs a guarantee as to what specific exception(s) could possibly be thrown from each of your methods and properties. Additionally, each exception must be uniquely identified. The handle, the client or caller uses to distinguish one exception from another is critical.

The items in the Error enum perform a few different purposes:

  1. Document the list of possible exceptions
  2. Provides a unique identifier for the caller
  3. Provides the basics for automatically generating the default message of the exception
  4. Provides the key for a possible custom message(s) that can be found in a resource file

Each item in the Error enum can optionally be backed by an entry in a resource file. This allows for custom message(s) to replace any or all default automatically generated messages.

Your class(es) will be hosted in an assembly (either a DLL or executable), and there should be at most one base resource file to contain all possible custom error messages for the classes in the assembly. Satellite resource files should be used to internationalization (globalization) your error messages.

Let’s explain:

      public void  Record(string aMsg)

      {

         AssertX.PreCond(User.Error.InternalInvalidArgument_, aMsg != null, “aMsg”);

 

   // Implementation …

      }

Any method which might throw an exception should use the AssertX class. This will funnel all exceptions through one single point in the AssertX class. This allows the developer to:

  1. Document things which must be true before the body of the method is executed
  2. Document things which must be true before returning to the caller
  3. Set one breakpoint to examine all application exceptions before being thrown.

The use of PreCond() and PostCond() provides clean, crisp and concise expression, while eliminated nasty clutter-some conditional “if(…)”statements.

NOTE: All of the AssertX methods take an optional number of parameters (param object[] aParms). These parameters are used in the parameter subsutition process. It is the developer’s responsibility to supply the correct number of and order of parameters for the specific Error enum used.

These explicit visual clues as to the requirements associated with each method are extremely helpful to the maintenance programmer for years to come. Additionally, a custom tool (Microsoft, are you listening?) could easily parse the code and include a XML comment of all possible exceptions that might be thrown by each and every method and/or property.

The AssertX class is responsible for causing the creation and throwing of the actual exception. The AssertX calls the Resource  class to create the exception, the Resource class checks the appropriate resource file for a custom message for the actual  Error enum passed. Each item in the Error enum can optionally be backed by an entry in a resource file. This allows for a custom message to replace the default automatically generated message. The resource file provides support for internationalization (globalization) of your error messages. A resource file breaks the dreaded tight coupling between programmer handling of the error and its message.

If no custom message is found, it builds a default message based on the  Error enum. Typically in the development process, the developer take a heads down approach and does not immediately create a custom message in the resource. This action can be taken at a later point in time, and sometime by someone other than a developer.

Next the Resource class performs any parameter substitution on the message. Finally, the Resource  class creates the exception and returns the instance to the AssertX class.

Before the  AssertX class throws the exception, it could log and/or email the exception.

How:

Developers using your class need an easy way to catch exception(s) specific to your class. They could have a try with a catch of System.Exception  or even of ExceptionX, but this is not very elegant. Adding a nested exception class to your class, , UUser,solves this problem.

So how does the nested class User.ExceptionX  come into play? The Resource class’ method  Build()  creates both the message for the exception and the exception itself. The method looks at the type information of the enum passed.

Example:

AssertX.PreCond(User.Error.InternalInvalidArgument_, aMsg != null, “aMsg”);

This will eventually call Resource. Build(User.Error.InternalInvalidArgument_) which will in turn look for an optional class called User.ExceptionX that inherits from CaberComputing.Utilities.ExceptionX.  If found then the Build() method will instantiate it, else instantiates CaberComputing.Utilities.ExceptionX.

 

The Resource.Build()  requires that all classes derived from CaberComputing.Utilities.ExceptionX.

 

CaberComputing.Utilities.ExceptionX must have a constructor with the following prototype:

public ExceptionX( System.Enum           aErrorType,

                   string                aMessage,

             System.Exception      aInnerException )

The ultimate form of misuse of the term “reuse” is to basically cut and copy the following code to any class where you want to define a nested exception class, which the Resource class wil use.

the Resource class wil use.

   public enum Error             // Master List

   {

// INSERT ITEMS HERE …

   }

 

   public class ExceptionX : CaberComputing.Utilities.ExceptionX

       {

              public ExceptionX(System.Enum      aErrorType,

       string                       aMessage,

       System.Exception             aInnerException )

                                 : base(aErrorType,  aMessage, aInnerException)

              {

              }

       }

 

Adding a snippet to Visual Studio to insert the above code into you class can ease the pain of the approach.


As a designer of a class library, HOW do you ensure your code behaves correctly? Unit Test, of course. Unit test provide other developers excellent examples as to how to use your class. All unit tests relative a single assembly should be placed in one test project. I like the test project name to be zTest.{AssemblyName}. This forces all of my test projects to be displayed at the end of the list of all projects in the Solution Explorer window of Visual Studio.

Your unit test should have at least one test for each method in your class to ensure successful execution and, additionally, one test for each possible exception. Typically, I like to code the test for the exceptions first, and prove they all work, then test one or more successful scenarios for each method.

So let’s get to an example for the User. Record() method.

namespace CaberComputing.Utilities.Tests

{

   using System;

   using NUnit.Framework;

 

   [TestFixture]

   public class UserTests

   {

      [Test]

      [ExpectedException(typeof(User.ExceptionX))]

      public void Record_InternalInvalidArgument()  // ß Name of expected error

      {

  User    user = new User();

  string  msg = null;                     // Causes the error

     

  user.Record( msg );

      }

 

      [Test]

      public void Record()

      {

   User    user = new User();

   string  msg = “Big John”;

     

   user.Record( msg );

      }

   }

}

Unfortunately, NUnit support for testing of expected exceptions is less than desired. The first test Record_InternalInvalidArgument() will in fact throw an exception of the type User­.­ExceptionX, , but what is really desired is for NUnit to verify the exception is a User.­Error.­InternalInvalidArgument

Currently, NUnit 2.2.4 does support the following prototype: [ExpectedException(Type exceptionType, string Message)]. This is insufficient since our messages can be contained in a resource file and developers neither maintain nor control the text associated with any message. Yes, as developer, I could get the text from the resource file and use that, but a great many of our messages require parameter substitution at runtime.

The following is an interface, I have suggested to the NUnit folks:

[ExpectedException(Type exceptionType, string propertyName, object propValue)]

 [Test]

 [ExpectedException(typeof(User.ExceptionX),

                      “ErrorType”, User.Error.InternalInvalidArgument)]

 public void Record_InternalInvalidArgument()  // ß Name of expected error

 { … }

Notes:

When is special attention/handling required?

Strategic classes need special attention, and they are:

  1. Classes which are an entry points to a physical and/or logical tier
  2. Any class which encapsulates a single business process

Special processing:

  1. The methods of these classes need to be wrapped with a try/catch with optional logging.
  2. When necessary, transform (re-tag) any exception which exposes a detail which the caller should not see.
  3. Throw a new exception versus re-throwing an exception, so as not to expose the stack trace associated with the original exception.

If you derived a class from another class which has virtual methods, when you override a virtual method you should/must adhere to the interface of the base class’s method. This includes the list of possible exceptions which might be thrown from those virtual methods. You cannot throw any exceptions other than those explicitly documented in the base class. When designing classes with virtual methods, adding a “catch-alll” error type (e.g. InternalUnknownErrorOccurred) to the list of possible exceptions (is a good idea, can be helpful).

Steven R. McCabe

Author profile:

Steven has been in the software business for over 20 years. He is a Microsoft Certified Solution Developer for .NET (MCSD for .NET). He is the President of Caber Computing, Inc. Steven is married with two teenage children. He loves golf, soccer and recently started throwing the rock (curling).

Search for other articles by Steven R. McCabe

Rate this article:   Avg rating: from a total of 13 votes.


Poor

OK

Good

Great

Must read
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: O-NO-YOU DON'T
Posted by: Anonymous (not signed in)
Posted on: Wednesday, January 23, 2008 at 7:39 AM
Message: TRUE:
- all apps require excellent exception handling
- single point of raising exception are ok but not a requirement (VS really rocks at breaking when an exception is raised or unhandled)
- tier classes require special handling (maybe maybe a list bur probably not)
- don't break the encapsulation =) OO-principle no-1.

FALSE:
- exception messages are NOT for end users, EVER! they are developer internal
- available exceptions per class doesn't work, you will spend all your time maintaining this list. (Check A. Heijlsbergs comments on Java for att code-ified version) or attempting not to break it... (since .net doesn't; how can you?)

MY-VERSION:
- don't catch unless you can handle it
- don't catch without a trace
- throw .net exceptions such as ArgumentNullException etc.
- define a FEW set of exceptions that can be throw for real errors (not Argument etc)
- ALWAYS include the inner exception or use throw;
- ALWAYS use using( ... ) never use try/finally
(write scope classes that utilize using( ... ))
- FINALLY BUT MOST IMPORTANT AND REALLY HARD TO IMPLEMENT. If an method throws an exception, leave the object in the same state as before the call...

best regards,
Daniel Petersson (d.m.petersson@gmail.com)

ps. I like you assert classes, I call mine Guard(s) since it is the shortest word I have found =)

Subject: Some good tips, but some very bad ones too
Posted by: Anonymous (not signed in)
Posted on: Wednesday, January 23, 2008 at 5:24 PM
Message: There are several portions of this article which completely contradict the recommendations in Cwalina and Abrams' Framework Design Guidelines. While some of this article's recommendations are good, others will lead to some seriously problematic issues (e.g. with API versioning). Specifically, using an "Error" Enum as a property of a single ExceptionX class that is used for everything is basically no different than using Error Codes instead of Exceptions. The Framework Design Guidelines book describes many reasons of why not to do these things, and you can find many other reasons described in various articles online.

If you are unsure of how to "properly" use Exceptions, DO NOT use this article as your guide.

Subject: Exceptions are not error reporting
Posted by: Anonymous (not signed in)
Posted on: Wednesday, January 23, 2008 at 7:55 PM
Message: Exceptions are EXCEPTIONAL and not for end user error reporting or flow control. Wrapping system exceptions in a single exception class is probably the worst advice ever. This leads to the typical anti-pattern of wrapping all code in a try..catch(Exception ex). This leads to hidden bugs when developers catch all exceptions and wind up ignoring them. Only catch the most specific exception you are prepared to handle -- which is probably very few.

Oh yeah and you don't need to inherit from ApplicationException that was a recommendation for .NET 1.1 which is no longer the case later version.

Subject: Hunting exceptions
Posted by: Robyn Page (view profile)
Posted on: Thursday, January 24, 2008 at 3:25 AM
Message: Steve writes 'Additionally, a custom tool (Microsoft, are you listening?) could easily parse the code and include a XML comment of all possible exceptions that might be thrown by each and every method and/or property.'
I haven't used it yet, but doesn't 'Exception Hunter' already do this? I quote ...
'you can analyze all methods in an assembly in one go, and you can produce a report showing the exceptions for each method. It can also show the reverse with one of the HTML reports, showing the methods for each exception'.
... I'm not sure what it would make of the exceptions of an application that used Steve's design, though.

Subject: Exception Hunter
Posted by: Brian Donahue (view profile)
Posted on: Thursday, January 24, 2008 at 9:05 AM
Message: Hi Robyn,

Exception Hunter can find and document all types of unhandled exceptions that can occur in a method. It works best if the exceptions thrown all have their own unique type identifier, though, because deriving all exceptions from ExceptionX and then catching ExceptionX in the code would flag all different kinds of exceptions as handled, if I understand this correctly.

Subject: Exception Hunter
Posted by: Bart Read (view profile)
Posted on: Thursday, January 24, 2008 at 10:04 AM
Message: I think I might post a more detailed response to this on my blog when I get a chance, however for now I just wanted to expand on what Robyn has said.

The best way to get this kind of functionality is to use the Exception Hunter command line (hunt.exe) to generate an XML report for you. You can then merge these results into the XML documentation file generated for your assembly (you'll need to write a tool to do this, although I can see us providing this function in the future).

Once you've done that you can use Sandcastle to generate HTML documentation.

Finally, with the merging you could also apply some custom formatting so that the unhandled exceptions are more obvious in the final documentation.

Subject: Reply to previous posts
Posted by: Steven R. McCabe (not signed in)
Posted on: Saturday, January 26, 2008 at 9:58 AM
Message: Exceptions have been one the most neglected topic in all of programming. I welcome everyone comments. Knowing specifically what points in the article you agree and disagree is critical to developing a better approach. For each point please state whether you agree or disagree with and provide specific reason(s). Don't just point to book, point to specifics points from that book, include a chapter and page number, then everyone can get involved. My goal is to improve how all developers approach and handle exception.


--------------------------------------------------
Reply to "O-NO-YOU DON'T"

I have emailed Daniel Petersson, requested details on his reference, since I was unable to find anything quickly.

- don't catch unless you can handle it
I agree. My intension of re-tagging was to minimize the knowledge required by the consumer of your class, and not to impose additional dependencies lower level exceptions. My goal was that the consumer only needed knowledge about your class and not the exceptions created in lower level modules. After looking at with Redgate's ExceptionHandler, I am coming of the opinion re-tagged by the developer of a module it nearly impossible and impractical I believe in order to achieve the goal, the compiler needs to assist in this effort. Exception(s) thrown by lower level components should only be handled when you’re going to do something, otherwise these exceptions should be re-tagged by the runtime engine as something like System.UnplannedException whose inner exception would contain the actual exception.
I would love to see an addition to the C# language. The C++ language specification has something similar to my wish. The ability to optionally define as a part of your method's interface the exceptions which can be bubbled back from your method. Any other any exception(s) thrown but not defined in the interface would be re-tagged by the runtime engine as an UnplannedException whose inner exception would contain the actual exception.
Typically, your method a call a method with in turn calls another component's method, which in turn calls another component's method. etc... As consumer of a class, I am not aware of, nor cannot be required to know of any actions/exceptions beyond what is documented in the class's interface, I am calling.
Take any one of your methods which might have 20 or more lines of code and run ExceptionHunter by Redgate, I think you will be unpleasantly surprised by the vast number of exceptions that are thrown by lower and lower level components. See: http://www.red-gate.com/MessageBoard/viewtopic.php?t=6177
Only exception FORCEFULLY thrown by my code should be placed in the interface definition of the method. Wouldn't it be great if the IDE or a tool like ExceptionHunter would updated this portion of the interface as it typed in code for my method.

- don't catch without a trace
Great idea!

- throw .net exceptions such as ArgumentNullException etc.
My feeling is the consumer of my method needs to quickly and clearly be able to identify the exceptions which my method has FORCEFULLY thrown from exceptions thrown by other components which my method utilizes. If my method used ArgumentNullException like the other components my method utilizes, then the consumers can not quickly and easily exceptions that are truly important to the consumer(caller).

- define a FEW set of exceptions that can be throw for real errors (not Argument etc)
I agree with the fewest number of exception possible.
- ALWAYS include the inner exception or use throw;
Generally, agree with this exception, when the inner exception would contain information that should not be disclosed to the caller.

- ALWAYS use using( ... ) never use try/finally (write scope classes that utilize using( ... ))
Great rule.

- FINALLY BUT MOST IMPORTANT AND REALLY HARD TO IMPLEMENT. If a method throws an exception, leave the object in the same state as before the call...
Yes, this is what I mention, but you have said it better and more clearly.

--------------------------------------------------

Reply to "Some good tips, but some very bad ones too"

I will comment on this after I have read the book.
If you could please refer to specific page(s) # in this book so support your statement, it would be beneficial to all.

--------------------------------------------------

Reply to "Exceptions are not error reporting"

Your statement "Exceptions are EXCEPTIONAL" is like motherhood (Giving birth to idea, but yet providing no details). Let's try to define concrete items. While I do not expect there always to be one fixed list of items, there are some that come to mind. Of the top of my head, an exception should be thrown when a method cannot complete to designated task because of:
1) Caller supplied bad argument(s)
2) The object is in an inappropriate state to complete the operation
3) A lower level operation could not be complete successfully

Your statement "not for end user error reporting", is consistent with the present day mindset. The particular point here, I would like to make, is developers should NOT use the message of the exception to make runtime decisions. Yes, the message, can always be used by the developer when debugging a problem. I would to qualify a point in the article, and suggest developers take an alternate view and look at the possibilities regarding the business layer and its exceptions and their messages being end user friendly and where appropriate displayed to the end user by the GUI.

Your statement "Wrapping system exceptions in a single exception class is probably the worst advice ever", please my reply to Daniel Petersson.

Your statement, "This leads to the typical anti-pattern of wrapping all code in a try..catch(Exception ex).". Yes, I am for minimizing the use of try..catch. As I stated " It is strongly suggested to try to use Approach 3 first and then when necessary employ Approach 2(re-tagging), and seldom if ever use Approach 1"

Your statement, "Only catch the most specific exception you are prepared to handle", please my reply to Daniel Petersson.


 

Top Rated

Acceptance Testing with FitNesse: Multiplicities and Comparisons
 FitNesse is one of the most popular tools for unit testing since it is designed with a Wiki-style... Read more...

Acceptance Testing with FitNesse: Symbols, Variables and Code-behind Styles
 Although FitNesse can be used as a generic automated testing tool for both applications and databases,... Read more...

Building Performance Metrics into ASP.NET MVC Applications
 When you're instrumenting an ASP.NET MVC or Web API application to monitor its performance while it is... Read more...

Acceptance Testing with FitNesse: Documentation and Infrastructure
 FitNesse is a popular general-purpose wiki-based framework for writing acceptance tests for software... Read more...

TortoiseSVN and Subversion Cookbook Part 11: Subversion and Oracle
 It is only recently that the tools have existed to make source-control easy for database developers.... Read more...

Most Viewed

A Complete URL Rewriting Solution for ASP.NET 2.0
 Ever wondered whether it's possible to create neater URLS, free of bulky Query String parameters?... Read more...

Visual Studio Setup - projects and custom actions
 This article describes the kinds of custom actions that can be used in your Visual Studio setup project. Read more...

.NET Application Architecture: the Data Access Layer
 Find out how to design a robust data access layer for your .NET applications. Read more...

Calling Cross Domain Web Services in AJAX
 The latest craze for mashups involves making cross-domain calls to Web Services from APIs made publicly... Read more...

Web Parts in ASP.NET 2.0
 Most Web Parts implementations allow users to create a single portal page where they can personalize... Read more...

Why Join

Over 400,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.