Igor Brejc
Igor Brejc

Reputation: 19004

C#: Overriding ToString() method for custom exceptions

I have a custom exception class which contains some additional fields. I want these to be written out in the ToString() method, but if I implement my own ToString(), I loose some other useful stuff (like writing the exception type name, the inner exception data and the stack trace).

What is the best way/pattern to implement your own ToString() method for such exceptions? Ideally it should reuse the existing mechanism, but be formatted in way similar to the default ToString() implementation.

UPDATE: prepending or appending my custom fields to the base.ToString() text isn't ideal IMHO, for example

PimTool.Utilities.OERestServiceUnavailableException: test ---> System.InvalidOperationException: inner message
   --- End of inner exception stack trace ---
   at PimTool.Tests.Services.OE.OERestClientTests.ExceptionsLogging() in D:\svn\NewPimTool\PimTool.Tests\Services\OE\OERestClientTests.cs:line 178, 
   StatusCode=0, message='test', requestId='535345'

means the custom fields are written at the end of the (potentially long) exception description. On the other hand, I want the exception type to be the first information written in the description.

UPDATE 2: I've implemented a solution for this, look for my own answer below.

Upvotes: 36

Views: 14824

Answers (8)

Vasiliy Zverev
Vasiliy Zverev

Reputation: 643

Similar idea to Glinko's answer of insertion of your variables after the first line of base string, a bit different implementation:

public override string ToString()
{
    string baseStr = base.ToString();
    return baseStr.Insert(baseStr.IndexOf('\n') + 1,
        string.Format("HttpStatusCode: {0}, TSError400: {1}\r\n",
        (int)HttpStatusCode,
        JsonConvert.SerializeObject(TSError400)));
}

Upvotes: 1

Glinko
Glinko

Reputation: 21

The simplest way

public override string ToString()
{
    StringBuilder sb = new();
    var separator = new string[] { Environment.NewLine };
    var str = base.ToString().Split(separator, 2, StringSplitOptions.None);
    sb.AppendLine(str[0]);

    // Your properties

    sb.Append(str[1]);
    return sb.ToString();
}

Upvotes: 2

Daryl
Daryl

Reputation: 18895

This is all overkill. Your exception should just override the Message Property.

public override String Message {
    get {  
        return base.Message + String.Format(", HttpStatusCode={0}, RequestId='{1}'", 
                    httpStatusCode, 
                    RequestId);
    }
}

The default ToString method for the Exception class is basically "ClassName: Message --> InnerException.ToString() StackTrace". So overriding the Message puts your message text exactly where it should be.

Upvotes: 44

Igor Brejc
Igor Brejc

Reputation: 19004

OK, this is what I came up with. I've implemented an extension class which replicates the original mechanism for formatting exceptions, but with a twist: a custom Action delegate which provides a plug-in for formatting custom fields:

public static class ExceptionFormatterExtensions
{
    public static string ExceptionToString (
        this Exception ex, 
        Action<StringBuilder> customFieldsFormatterAction)
    {
        StringBuilder description = new StringBuilder();
        description.AppendFormat("{0}: {1}", ex.GetType().Name, ex.Message);

        if (customFieldsFormatterAction != null)
            customFieldsFormatterAction(description);

        if (ex.InnerException != null)
        {
            description.AppendFormat(" ---> {0}", ex.InnerException);
            description.AppendFormat(
                "{0}   --- End of inner exception stack trace ---{0}",
                Environment.NewLine);
        }

        description.Append(ex.StackTrace);

        return description.ToString();
    }
}

Now you can use this method in your own ToString() implementations without duplicating the formatting code:

    public override string ToString()
    {
        return this.ExceptionToString(
            description =>
            {
                description.AppendFormat(
                    ", HttpStatusCode={0}, RequestId='{1}'", 
                    httpStatusCode, 
                    RequestId);
            });
    }

Upvotes: 16

Konamiman
Konamiman

Reputation: 50273

You could manually add the default data to the string returned by ToString, by looking at the exception properties. For example, the following will simulate the data returned by default by a exception's ToString method (assuming there are no inner exceptions):

string.Format("{0}: {1}\r\n{2}", this.GetType().Name, this.Message, this.StackTrace);

Or, you could simply append (or prepend) the data returned by base.ToString to the information you want to add.

Upvotes: 14

Andrew Rollings
Andrew Rollings

Reputation: 14551

If you're primarily looking at them in the debugger, then you can use the [DebuggerDisplay] attribute to specify their formatting and not touch the existing ToString method.

Otherwise, just overload ToString and be sure to call the base class version base.ToString()

Upvotes: 5

Chris Fulstow
Chris Fulstow

Reputation: 41872

You can override the ToString() method to include your own custom information, and still call the default base Exception ToString() like this:

public class MyException : Exception
{
    public string CustomField { get; set; }
    public override string ToString()
    {
        return CustomField + Environment.NewLine + base.ToString();
    }
}

Upvotes: 12

Fried Hoeben
Fried Hoeben

Reputation: 3272

Inside the override call base.ToString() and modify the resulting string to your needs...

Upvotes: 1

Related Questions