.net - 如何更改Exception对象的异常消息?

Ian*_*oyd 26 .net exception

如何Message在C#中更改Exception对象?

奖金Chatter

Message属性Exception只读的:

public virtual string Message { get; }
Run Code Online (Sandbox Code Playgroud)

补充阅读

在PHP中,同样的问题被回答为"你不能",但是给出了一个解决方法:

但是,您可以确定它的类名和代码,并使用相同的代码但使用不同的消息抛出同一类的新类.

如何确定异常的类名,并在C#中使用不同的消息抛出同一个类的新类?

例如:

catch (Exception e)
{
   Exception e2 = Activator.CreateInstance(e.GetType());
   throw e2;
}
Run Code Online (Sandbox Code Playgroud)

不起作用,因为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);
}
Run Code Online (Sandbox Code Playgroud)

除了现在我的代码强制依赖于不会出现在每个解决方案中的程序集.

此外,现在异常似乎来自我的代码,而不是抛出它的行; 我丢失了例外的位置信息

Ric*_*ard 30

您创建一个Exception具有新消息的新(或更好的特定子类型)(并将原始异常作为传递InnerException).

例如.

throw new MyExceptionType("Some interesting message", originalException);
Run Code Online (Sandbox Code Playgroud)

NB.如果你真的想要使用,Activator.CreateInstance你可以使用可以传递参数的重载,但是不能依赖不同的Exception派生类型来为构造函数重载(message, innerException).

  • 抛出新异常的问题是在抛出新异常的地方抛出新异常.如果有一种方法可以将我的新`throw`的堆栈替换为`InnerException`,或者一种方法来追溯当前异常的来源,它会起作用.我**已经抛出了我自己的自定义异常,但这是一个临时的解决方法,我需要一个真正的解决方案[(因此SO问题)](http://stackoverflow.com/questions/9334182/net-how-到变化异常消息的的异常对象) (3认同)
  • 但是,"InnerException"仍然是可检索的,它保留了原始堆栈跟踪(以及原始异常详细信息的整个上下文,如果您还需要其他内容).`.InnerException`只是给你底层的`Exception`实例. (2认同)

Ian*_*oyd 17

我在一个新闻网站 链接的博客文章中找到了解决方案:

catch (Exception e)
{
   Exception e2 = (Exception)Activator.CreateInstance(e.GetType(), message, e);
   throw e2;
}
Run Code Online (Sandbox Code Playgroud)

它并不完美(你丢失了堆栈跟踪); 但这就是.NET的本质.

  • 从理论上讲,异常类型可能没有带有两个参数的ctor. (4认同)

Pie*_*rez 8

您可以通过反射更改异常消息,如下所示......

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);
Run Code Online (Sandbox Code Playgroud)

所以你可以创建一个扩展方法......

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后用它...

...
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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

你必须记住,如果你使用“throw ex;” 堆栈跟踪将会丢失

为了避免这种情况,你必须使用“throw;” 无一例外


dev*_*rts 7

我只是想在这里与Ian相关的答案.如果您在博客文章中使用该技术,您将不会丢失堆栈.是的,您将丢失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);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

如果您获取异常的完整字符串,您将获得:

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
Run Code Online (Sandbox Code Playgroud)

所以你无论如何都得到了整个堆栈,还有额外的信息


Red*_*dog 5

您可以使用新消息将先前的异常包装在一个新的异常中,并使用堆栈跟踪/等的内部异常。

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

  • 应该保留内部异常的堆栈跟踪,以便您可以在那里查找。 (3认同)