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