Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Cloning an Exception (kinda sorta)

4.75/5 (3 votes)
10 Apr 2011CPOL2 min read 22K  
Using Reflection to instantiate an Exception of a given type
Sometimes, when you catch an Exception, you just want to add some information and re-throw. One way to do that is to add items to the Exception's Data collection, but I don't think many developers do that (I don't).

The other way to do that is to instantiate and throw a new Exception. When doing this, you should include the original Exception as an InnerException -- but all too often developers don't :sigh: .

One good thing about this technique is that you can throw a different (hopefully more-descriptive) type of Exception. Buuut... all too often, the developer simply instantiates a base Exception, which hinders the ability to catch and handle the Exception properly.

And, when you combine the poor practice of replacing a derived Exception with a base Exception with not including the original Exception as an InnerException, you create a situation where tracking down and handling an Exception is rather difficult (yes, I was put in this situation this week).

So, what can a developer do to throw a proper Exception? Obviously, you can catch different types of Exception and throw based on that. But that may not be the best general solution.

As I thought about this situation this week, I saw that a little bit of Reflection would allow me to instantiate an Exception of the same type that was caught (in most cases), so I wrote the following:

namespace PIEBALD.Lib.LibExc.Clone
{
    public static partial class LibExc
    {
        private static readonly System.Collections.Generic.Dictionary
            <System.Type,System.Reflection.ConstructorInfo> con ;

        private static readonly System.Type[] sig ;

        static LibExc
        (
        )
        {
            con = new System.Collections.Generic.Dictionary
                <System.Type,System.Reflection.ConstructorInfo>() ;

            sig = new System.Type[]
            {
                typeof(string)
            ,
                typeof(System.Exception)
            } ;

            return ;
        }

        public static System.Exception
        Clone
        (
            this System.Exception Source
        ,
            string                Message
        ,
            params object[]       Params
        )
        {
            System.Type typ = Source.GetType() ;

            if ( !con.ContainsKey ( typ ) )
            {
                System.Reflection.ConstructorInfo temp = typ.GetConstructor
                (
                    System.Reflection.BindingFlags.Public
                    |
                    System.Reflection.BindingFlags.Instance
                ,
                    null
                ,
                    sig
                ,
                    null
                ) ;

                if ( temp == null )
                {
                    throw ( new System.Exception ( "No constructor found for type " + typ.Name , Source ) ) ;
                }

                con [ typ ] = temp ;
            }

            return ( (System.Exception) con [ typ ].Invoke
            (
                new object[]
                {
                    System.String.Format ( Message , Params )
                ,
                    Source
                }
            ) ) ;
        }
    }
}


I called it Clone even though it's not a true clone operation, but I think it conveys the basic idea of what it does.

Also, not all Exception types have public constructors that take a string and an Exception (System.Data.SqlClient.SqlException
for instance). In these cases, the above code will throw an Exception, but that may not be the best solution. I just haven't yet decided what I want to do: I could have a fall-back Exception type, I could allow the developer to specify a fall-back Exception type, or I can search the parent types until a suitable type is found. Please post opinions of what you think should be done.

I also have a VB.NET version, but it's uuugly! :D

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)