C#: переопределение метода ToString() для пользовательских исключений

У меня есть собственный класс исключений, который содержит некоторые дополнительные поля. Я хочу, чтобы они были записаны в методе ToString(), но если я реализую свой собственный ToString(), я потеряю некоторые другие полезные вещи (например, написание имени типа исключения, внутренних данных исключения и трассировки стека).

Каков наилучший способ/шаблон для реализации собственного метода ToString() для таких исключений? В идеале он должен повторно использовать существующий механизм, но быть отформатирован аналогично реализации ToString() по умолчанию.

ОБНОВЛЕНИЕ: добавление или добавление моих настраиваемых полей к тексту base.ToString() не идеально, ИМХО, например

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'

означает, что настраиваемые поля записываются в конце (потенциально длинного) описания исключения. С другой стороны, я хочу, чтобы тип исключения был первой информацией, написанной в описании.

ОБНОВЛЕНИЕ 2: я реализовал решение для этого, ищите мой собственный ответ ниже.


person Igor Brejc    schedule 11.12.2009    source источник
comment
Я ищу то же самое, я хочу добавить дополнительную информацию снаружи, а не в поле «Сообщение», но я хочу, чтобы формат ToString сохранялся со всей трассировкой стека и внутренними исключениями. Было бы интересно увидеть код для Exception.ToString() и скопировать его, добавив дополнительную информацию.   -  person pauloya    schedule 14.01.2010
comment
Ну, код охватывает большинство вещей, которые вы упомянули. Хотя позже я вспомнил, что мог бы использовать словарь Exception.Data для хранения своих пользовательских материалов. Я предполагаю, что в этом случае это будет отображаться с реализацией ToString() по умолчанию, но я еще не пробовал.   -  person Igor Brejc    schedule 15.01.2010
comment
Связанный пост - Почему и когда было бы уместно переопределить ToString?   -  person RBT    schedule 06.09.2018


Ответы (7)


Это все перебор. Ваше исключение должно просто переопределить свойство сообщения.

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

Метод ToString по умолчанию для класса Exception в основном "ClassName: Message --> InnerException.ToString() StackTrace". Таким образом, переопределение сообщения помещает текст вашего сообщения именно туда, где он должен быть.

person Daryl    schedule 07.11.2014
comment
Это работает, если только вы не пойдете по этому пути, чтобы внедрить пользовательскую трассировку стека. - person yoyo; 17.10.2015
comment
@yoyo, да, но это не было требованием ОП - person Daryl; 17.10.2015
comment
Совершенно верно, и ваш ответ хороший. (Я пришел сюда в поисках чего-то немного другого.) - person yoyo; 17.10.2015
comment
Это работает хорошо, но я не уверен, что это отличный стиль, по крайней мере, может быть, не всегда, потому что свойство Message не ограничено использованием ToString. new CustomException("My message").Message может неожиданно вернуть строку != "My message". Это может кого-то укусить, если они хотят где-то отобразить / зарегистрировать свое сообщение, а не дополнительную информацию, и предположить, что Message равно установленному сообщению. Не говорю, что это неправильно, но что-то рассматривать в каждом конкретном случае. - person xr280xr; 03.03.2021

Вы можете вручную добавить данные по умолчанию в строку, возвращаемую ToString, просмотрев свойства исключения. Например, следующий пример имитирует данные, возвращаемые по умолчанию методом исключения ToString (при условии отсутствия внутренних исключений):

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

Или вы можете просто добавить (или добавить) данные, возвращаемые base.ToString, к информации, которую вы хотите добавить.

person Konamiman    schedule 11.12.2009
comment
Кажется, это самое близкое решение к тому, что я имел в виду. Хотя вы забыли написать InnerException;) - person Igor Brejc; 11.12.2009

Хорошо, это то, что я придумал. Я реализовал класс расширения, который воспроизводит исходный механизм форматирования исключений, но с изюминкой: настраиваемый делегат Action, предоставляющий подключаемый модуль для форматирования настраиваемых полей:

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();
    }
}

Теперь вы можете использовать этот метод в своих собственных реализациях ToString() без дублирования кода форматирования:

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

Вы можете переопределить метод ToString(), чтобы включить свою собственную пользовательскую информацию, и по-прежнему вызывать базовое исключение ToString() по умолчанию следующим образом:

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

Если вы в первую очередь просматриваете их в отладчике, вы можете использовать атрибут [DebuggerDisplay], чтобы указать их форматирование и не трогать существующий метод ToString.

В противном случае просто перегрузите ToString и обязательно вызовите версию базового класса base.ToString()

person Andrew Rollings    schedule 11.12.2009
comment
Хотя это не то, что я искал, +1 за упоминание об этом. - person Igor Brejc; 11.12.2009

Внутри переопределения вызовите base.ToString() и измените полученную строку в соответствии с вашими потребностями...

person Fried Hoeben    schedule 11.12.2009

Самый простой способ

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();
}
person Glinko    schedule 21.03.2021