Подтвердить что ты не робот

.net - Как изменить сообщение об исключении объекта Exception?

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

Бонус-чат

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

public virtual string Message { get; }

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

На тот же вопрос, на PHP, был дан ответ: "Вы не можете", но дал обходное решение:

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

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

например:.

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

Eg.

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

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

Ответ 3

Я просто хотел перезвонить здесь, чтобы Иан рассказал о своем ответе. Если вы используете технику в блоге , вы не потеряете стек. Да, вы потеряете стек в члене 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

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

try
{
    throw new Exception("This error message sucks");
}
catch (Exception e)
{
    throw new Exception("There error message is prettier", e);
}

Ответ 5

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

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

Ответ 6

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

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

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

Ответ 7

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

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;" без исключения.

Ответ 8

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

public class InvalidGraphicTypeException : Exception
{

   public InvalidGraphicTypeException(Graphic badType) : 
      base("Invalid Graphic Type: " + badType.GetType().Name)
   {
   }
}