8

I want a central place to extract information from an exception, set all the information I need to its message parameter and then rethrow that information as an Exception of the same type.

The better solution probably would be to do this at the place where the exception is finally being handled (and its message logged), but.. I have control over the place throwing the exception, and not over the place that receives the exception and only logs its Message content.

Apart from that design decision and given that message is a readonly property, I would have (?) to create a new Exception object in some way, is there a way to make the new exception object the same type as the original one?

Here is my code, which does not compile - it stumbles over the throw line (where I try to dynamically cast the object).

public static void RethrowExceptionWithFullDetailInMessage(string msg, Exception ex)
{
    Exception curEx = ex;
    int cnt = 0;
    while (curEx != null)
    {
        msg += "\r\n";
        msg += cnt++ + " ex.message: " + curEx.Message + "\r\n";
        msg += "Stack: " + curEx.StackTrace;
        curEx = curEx.InnerException;
    }
    object newEx = Convert.ChangeType(new Exception(msg), ex.GetType());
    throw (ex.GetType())newEx;
}

Does this

throw (Exception)newEx;

preserve the type? (It compiles.)

Does the Convert.ChangeType make sure I get an Exception of the correct type?

Andreas Reiff
  • 7,961
  • 10
  • 50
  • 104
  • 1
    At a glance it seems that what you are trying to do here in terms of the message is already done by the `Exception.ToString()` method. You will lose a certain amount of context and make debugging much harder if you generate and throw a new exception like this (your break will happen in this method instead of where the actual exception occured). Also I'd think you probably need a generic method to generate the correct type but I may be wrong... – Chris Jul 26 '12 at 14:24
  • Have you walked through the debugger with the simpler `throw (Exception)newEx` to see if it keeps the type? – BlackVegetable Jul 26 '12 at 14:26
  • Exception.ToString works great. The output takes a little getting used to, but apart from that, it seems to do exactly what I want. (Only for future coding, not for the problem at hand, or for then assigning it to message.) – Andreas Reiff Aug 02 '12 at 13:08
  • To add a comment to all answers here: what a great example to get some really insightful answers to a bad (design) question!! At least now I know for sure how to start something similar in future. Plus, I am always curious to learn a bit about reflection, which I certainly did today. Many thanks to all of you! – Andreas Reiff Aug 02 '12 at 13:49

5 Answers5

12

What you are trying to do here is not as easy as it seems and there are lots of pitfalls to consider.

Remember, that Convert.ChangeType() will convert one type to another (assuming such a path exists, like converting a string to an int for example). Most exceptions wont do this (Why would they?)

IN order to pull this off, you would have to examine the exception type at runtime with the GetType() method and locate a constructor that has requirements you can satisfy and invoke it. Be careful here, since you don't have control over how all exceptions are defined there is no guarantee you will have access to "standard" constructors.

That being said, if you feel like being a rule breaker you could do something like this...

void Main()
{
    try
    {   
        throw new Exception("Bar");
    }
    catch(Exception ex)
    {
        //I spit on the rules and change the message anyway
        ex.GetType().GetField("_message", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(ex, "Foo");
        throw ex;
    }
}
iamkrillin
  • 6,798
  • 1
  • 24
  • 51
  • That is one nice dirty-hack solution. Also thx for the insight on Convert. I take it that _message is the underlying field for the property Message?! Anyway, tried and it is working. – Andreas Reiff Aug 02 '12 at 13:12
  • You are correct, _message is the backing field for the Message property – iamkrillin Aug 02 '12 at 14:33
  • This worked for me and did exactly what i require, but it really upset "Resharper" with "Possible System.NullReferenceException" and "Exception rethrow possibly intended" warnings... – Dib Aug 20 '15 at 06:32
4

You could do this to dynamically call the constructor of the exception type:

object newEx = Activator.CreateInstance(ex.GetType(), new object[] { msg });

Your original code would fail at runtime, because for Convert.ChangeType towork, the exception type would have to implement IConvertible and support conversion to the other exception type, which i doubt.

Botz3000
  • 39,020
  • 8
  • 103
  • 127
  • 2
    You might want to have it check (or catch the potential exceptions from `Activator.CreateInstance`) to make sure that the particular exception type actually implements a `(string message)` constructor. I know many (if not all in the BCL, not sure), but it's not guaranteed. – Chris Sinclair Jul 26 '12 at 14:38
  • I just have to make sure to use throw (Exception)newEx;, and then everything works fine. Thx for the insight and ideas. – Andreas Reiff Aug 02 '12 at 13:13
  • This worked for me and even with "throw (Exception)newEx;" my calling code was still able to catch the *specific* "ArgumentException" that I needed to catch. – Dib Aug 20 '15 at 06:35
4

May be it's a bit late, but would this work for you?

catch (Exception ex)
{
    throw new Exception("New message", ex);
}
  • 2
    The problem with this approach, is if you catch a specific exception, like "System.ArgumentException", you are re-throwing a general exception of the "System.Exception" type. If you have code at the top of the call-chain expecting to catch the specific exception it wont. I just fell foul of this with my own code being written like your example! – Dib Aug 20 '15 at 06:30
1

You can change the exception message via reflection like this...

Exception exception = new Exception("Some message.");
var type = typeof(Exception);
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
var fieldInfo = type.GetField("_message", flags);
fieldInfo.SetValue(exception, message);

So you can create an extension method...

namespace ExceptionExample
{
    public static class ExceptionExtensions
    {
        public static void SetMessage(this Exception exception, string message)
        {
            if (exception == null)
                throw new ArgumentNullException(nameof(exception));

            var type = typeof(Exception);
            var flags = BindingFlags.Instance | BindingFlags.NonPublic;
            var fieldInfo = type.GetField("_message", flags);
            fieldInfo.SetValue(exception, message);
        }
    }
}

And then use it...

...
using static ExceptionExample.ExceptionExtensions;

public class SomeClass
{
    public void SomeMethod()
    {
        var reader = AnotherClass.GetReader();
        try
        {
            reader.Read();
        }
        catch (Exception ex)
        {
            var connection = reader?.Connection;
            ex.SetMessage($"The exception message was replaced.\n\nOriginal message: {ex.Message}\n\nDatabase: {connection?.Database}");
            throw; // you will not lose the stack trace
        }
    }
}

You have to keep in mind that if you use "throw ex;" the stack trace will be lost.

To avoid this you must use "throw;" without the exception.

Piero Álvarez
  • 300
  • 3
  • 7
0

Supplemental comment.

These all work in supplementing the exception message, but I found that using "throw" did NOT preserve the StackTrace - the last trace pointed to the actual "throw" statement (dropping the root cause location).

From discussions elsewhere, it's clear there are some circumstances that throw doesn't preserve due to CLR stack limitations

Throw and preserve stack trace not as expected as described by Code Analysis

Solution: dump the StackTrace in each exception (e.g. and add to the error message) and/or dump to logging