65.9K
CodeProject is changing. Read more.
Home

Designing Exception Hierarchy in C#

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.64/5 (8 votes)

Oct 9, 2006

5 min read

viewsIcon

50341

How Exception Hierarchy be designed in C#

Sample Image - Exception_CSharp.jpg

Introduction

In this article I will talk about the Exception Handling strategy in C#.

 

There are basically 4 types of error handling strategy

  1. Each function returning a status like HRESULT / bool.
  2. Setting a global variable / structure with error information
  3. Set Jump and Long Jump
  4. Exception Handling

 

There is no best strategy which satisfies each and every product needs. In some situations we need to mix these strategies as one single strategy does not apply. In this article I will only talk about how one should use exception strategy efficiently.

 

Assumption: Developer has a very good knowledge of exception handling classes in .NET

 

Some important points:

1.      Exceptions are not necessarily errors. Whether or not an exception represents an error is determined by the application in which the exception occurred. An exception that is thrown when a file is not found may be considered an error in one scenario, but may not represent an error in another.

2.      All exception classes should derive from the Exception base class

3.      ApplicationException serves as the base class for all application-specific exception classes

4.      And the most important: Avoid throwing Exceptions unnecessarily.

 

Refer the exception class hierarchy at the top of this article.

This hierarchy allows your application to benefit from the following:

1.      Easier development because you can define properties and methods on your Product base application exception class that can be inherited by your other application exception classes.

2.      New exception classes created after your application has been deployed can derive from an existing exception type within the hierarchy. Existing exception handling code that catches one of the base classes of the new exception object will catch the new exception without any modifications to the existing code, interpreting it as one of the object's base classes.

 

As you create your hierarchy, use the following questions to help you to decide if you need to create a new exception class:

  1. Does an exception exist for this condition?
  2. Does a particular exception need discrete handling?
  3. Do you need specific behavior or additional information for a particular exception?

 

Designing your Product Base Exception Class

/// <summary>

    /// Facility specifies from where the error has occurred Kept for COM error

    /// </summary>

    public enum Facility

    {

        Feature1,

        Feature2

    }

   

    /// <summary>

    /// This class defines the base type for all the custom exceptions thrown across the Product. All scenario based exceptions, should derive from BaseException class

    /// </summary>

    public class BaseException : ApplicationException

    {

        public BaseException(Facility facility, short errorNo) :base()

        {

            HResult = MakeHResult(facility,errorNo);        

        }

 

        public BaseException(Facility facility, short errorNo,string message) : base(message)

        {

            HResult = MakeHResult(facility,errorNo);

        }

     

        public BaseException(Facility facility, short errorNo, string message, Exception inner) : base(message, inner)

        {

            HResult = MakeHResult(facility,errorNo);

        }

     

        public BaseException(Facility facility, short errorNo,string message, string context) : base(message)

        {

            HResult = MakeHResult(facility,errorNo);

            this.context = context;

        }

     

        public BaseException(Facility facility, short errorNo, string message, string context, Exception inner) : base(message, inner)

        {

            HResult = MakeHResult(facility,errorNo);

         

            this.context = context;

        }

     

        public int ErrorCode

        {

            get

            {

                return this.HResult;

            }

            set

            {

                this.HResult = value;

            }

        }

 

        /// <summary>

        /// A property that exposes the ErrorContext. The contents of Context

        /// will vary upon the type of the exception and the context of the exception

        /// For e.g if the Exception pertains to a file it may hold the name of the file that

        /// is in error. Developers are expected to provide adequate data to identify the error context in detail.

        /// </summary>

        public string ContextData

        {

            get

            {

                return this.context;

            }

            set

            {

                this.context = value;

            }

        }

 

        /// <summary>

        /// Returns a HRESULT from the values of severity, facility and error number

        /// </summary>

        /// <param name="severity">The severity to be used</param>

        /// <param name="facility">The facility to be used</param>

        /// <param name="errorNo">The error number </param>

        /// <returns>A HRESULT constructed from the above 3 values</returns>

        protected int MakeHResult(Facility facility, short errorNo)

        {

            // Make HR

            int result = (int)1 << 31;

            result += (int) facility << 16;

            result += errorNo;

         

            return result;

        }

 

        private string context;

    }

 

Why error number?

 

Consider there are two error states, one FileNotFoundException and InvalidFilePathException. Instead of making two exception classes which encapsulate the same data, you can write an enumeration and just pass the scenarion like this:

public enum ErrorScenario

      {

        FileNotFound,

        InvalidFilePath

}

 

There is a lot to write on handling COM Error in C#. The following table maps HRESULT to its comparable exception class in the .NET Framework

 

 

 

HRESULT

.NET exception

COR_E_APPLICATION

ApplicationException

COR_E_ARGUMENT or
E_INVALIDARG

ArgumentException

COR_E_ARGUMENTOUTOFRANGE

ArgumentOutOfRangeException

COR_E_ARITHMETIC or
ERROR_ARITHMETIC_OVERFLOW

ArithmeticException

COR_E_ARRAYTYPEMISMATCH

ArrayTypeMismatchException

COR_E_DIVIDEBYZERO

DivideByZeroException

COR_E_FILENOTFOUND or
ERROR_FILE_NOT_FOUND

FileNotFoundException

COR_E_FORMAT

FormatException

COR_E_INDEXOUTOFRANGE

IndexOutOfRangeException

COR_E_INVALIDCAST or
E_NOINTERFACE

InvalidCastException

COR_E_MULTICASTNOTSUPPORTED

MulticastNotSupportedException

COR_E_NOTFINITENUMBER

NotFiniteNumberException

E_NOTIMPL

NotImplementedException

COR_E_NOTSUPPORTED

NotSupportedException

COR_E_NULLREFERENCE or
E_POINTER

NullReferenceException

COR_E_OUTOFMEMORY or

E_OUTOFMEMORY

OutOfMemoryException

COR_E_OVERFLOW

OverflowException

COR_E_PATHTOOLONG or
ERROR_FILENAME_EXCED_RANGE

PathTooLongException

COR_E_SECURITY

SecurityException

 

For more details, you can reach me at SumitkJain@hotmail.com