.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);
}
За исключением того, что теперь мой код заставляет зависимость от сборок, которые не будут присутствовать в каждом решении.
Кроме того, теперь исключение, похоже, исходит из моего кода, а не строки, которая его бросила; я теряю информацию о местоположении исключения
Ответы
Ответ 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)
{
}
}