.net - Как изменить сообщение об исключении объекта Exception? - PullRequest
26 голосов
/ 17 февраля 2012

Как я могу изменить Message объекта Exception в C #?

Bonus Chatter

Свойство Message Exception является только для чтения :

public virtual string Message { get; }

Дополнительное чтение

На тот же вопрос в PHP ответили «Вы не можете» , но дал обходной путь:

Однако вы можете определить его имя и код класса и добавить новый, того же класса, с тем же кодом, но с другим сообщением.

Как определитьимя класса исключения и добавьте новый в том же классе, но с другим сообщением, в C #?

, например:

catch (Exception e)
{
   Exception e2 = Activator.CreateInstance(e.GetType());
   throw e2;
}

не работает, потому что свойство Messageисключения только для чтения и .NET.См. Оригинальный вопрос.


Обновление

Я пытался перехватить каждый ожидаемый тип исключения:

try
{
    reader.Read();
}
catch (OleDbException e)
{
   throw new OleDbException(e, sql);
}
catch (SqlException e)
{
   throw new SqlException (e, sql);
}
catch (IBM.DbException e)
{
   throw new IBM.DbException(e, sql);
}
catch (OdbcException e)
{
   throw new OdbcException (e, sql);
}
catch (OracleException e)
{
   throw new OracleException (e, sql);
}

За исключением того, что теперь мой кодвызывает зависимость от сборок, которые не будут присутствовать в каждом решении.

Кроме того, теперь, похоже, исключение исходит из моего кода, а не из строки, которая его вызвала;я теряю информацию о местонахождении исключения

Ответы [ 8 ]

29 голосов
/ 17 февраля 2012

Вы создаете новый Exception (или - более специфичный подтип), который имеет новое сообщение (и передаете исходное исключение как InnerException).

Например.

throw new MyExceptionType("Some interesting message", originalException);

NB. Если вы действительно хотите использовать Activator.CreateInstance, вы можете использовать перегрузку, которой могут передаваться параметры, но нельзя полагаться на различные производные типы исключений, чтобы иметь перегрузку конструктора для (message, innerException).

17 голосов
/ 01 ноября 2012

Я нашел решение в посте в блоге , связанном с новостным сайтом :

catch (Exception e)
{
   Exception e2 = (Exception)Activator.CreateInstance(e.GetType(), message, e);
   throw e2;
}

Это не идеально (вы теряете свой след стека); но такова природа .NET.

7 голосов
/ 11 декабря 2012

Я просто хотел позвонить здесь, чтобы Йен рассказал о своем ответе.Если вы используете методику в блоге , вы не потеряете стек.Да, вы потеряете стек в элементе StackTrace последнего исключения, но вы не потеряете стек в целом из-за внутренних исключений.Смотрите здесь:

class Program
{
    static void Main(string[] args)
    {
        try
        {
            Test1();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }

    public static void Test1()
    {
        try
        {
            Test2();
        }
        catch (Exception ex)
        {
            throw ExceptionUtil.Rethrow(ex, "caught in test1");
        }
    }

    public static void Test2()
    {
        throw new Exception("test2");
    }

    public static class ExceptionUtil
    {
        public static Exception Rethrow(Exception ex, string message)
        {
            if (ex == null)
            {
                ex = new Exception("Error rethrowing exception because original exception is <null>.");
            }

            Exception rethrow;

            try
            {
                rethrow = (Exception)Activator.CreateInstance(ex.GetType(), message, ex);
            }
            catch (Exception)
            {
                rethrow = new Exception(message, ex);
            }
            return rethrow;
        }

        public static Exception Rethrow(Exception ex, string message, params object[] args)
        {
            string formatted;

            try
            {
                formatted = String.Format(message, args);
            }
            catch (Exception ex2)
            {
                formatted = message + "\r\n\r\nAn error occurred filling in exception message details:\r\n\r\n" + ex2;
            }
            return Rethrow(ex, formatted);
        }
    }

}

Если вы возьмете полную строку исключения, вы получите:

System.Exception: caught in test1 ---> System.Exception: test2
at ScratchPad2.Program.Test2() in C:\Projects\Experiments\ScratchPad2\Program.cs:line 36
at ScratchPad2.Program.Test1() in C:\Projects\Experiments\ScratchPad2\Program.cs:line 26
--- End of inner exception stack trace ---
at ScratchPad2.Program.Test1() in C:\Projects\Experiments\ScratchPad2\Program.cs:line 30
at ScratchPad2.Program.Main(String[] args) in C:\Projects\Experiments\ScratchPad2\Program.cs:line 14

Таким образом, вы все равно получите весь стек, плюс дополнительная информация

4 голосов
/ 17 февраля 2012

Вы можете заключить предыдущее исключение в новое с новым сообщением и использовать внутреннее исключение для трассировки стека / и т.д.

try
{
    throw new Exception("This error message sucks");
}
catch (Exception e)
{
    throw new Exception("There error message is prettier", e);
}
1 голос
/ 05 июня 2019

Вы можете изменить сообщение об исключении через отражение следующим образом ...

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

Таким образом, вы можете создать метод расширения ...

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

А потом используй это ...

...
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
        }
    }
}

Вы должны иметь в виду, что , если вы используете "throw ex;" трассировка стека будет потеряна .

Чтобы избежать этого вы должны использовать "throw;" без исключения .

1 голос
/ 27 октября 2017

Я знаю, что это очень старый вопрос, но я не думаю, что какой-либо из существующих ответов является идеальным (они скрывают детали исключений во внутренних исключениях или номерах строк маски).Подход, который я использую, состоит в том, чтобы создать исключение, используя статический метод фабрики вместо конструктора:

public class CustomException {
   public string SampleProperty { get; set; }

    public static CustomException FromAlert(Alert alert)
    {
        var ex = new CustomException(string.Format("{0}-{1}", alert.propertyA, alert.propertyB));
        ex.SampleProperty = "somethign else";
        return ex;
    }
}

Так что теперь вы можете бросить:

throw CustomException.FromAlert(theAlert);

ИИзмените сообщение на содержание вашего сердца.

1 голос
/ 17 февраля 2012

Лучший способ справиться с этим - написать собственный класс исключений, соответствующий этой конкретной ситуации.Тогда поймай исключение и брось свое собственное исключение.Возможно, вы захотите посмотреть здесь: http://msdn.microsoft.com/en-us/library/ms229064.aspx для получения дополнительной информации.

Если вы считаете, что исключение клиента не требуется, вы должны иметь возможность передать пользовательскую строку в конструктор Exception при создании исключения: http://msdn.microsoft.com/en-us/library/48ca3hhw.aspx

0 голосов
/ 05 мая 2014

По моему маленькому старому мнению, лучший способ сделать это - наследовать от Exception (или какого-либо другого класса исключений, предоставляемого фреймворком, в зависимости от ситуации), затем поместить строку сообщения в вызов базового конструктора, который принимает Message в качестве параметра.вот так:

public class InvalidGraphicTypeException : Exception
{

   public InvalidGraphicTypeException(Graphic badType) : 
      base("Invalid Graphic Type: " + badType.GetType().Name)
   {
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...