【问题标题】:.net - How to change exception message of Exception object?.net - 如何更改异常对象的异常消息?
【发布时间】:2026-01-13 17:05:02
【问题描述】:

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

奖金聊天

ExceptionMessage 属性是只读的

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

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

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

【问题讨论】:

    标签: .net exception


    【解决方案1】:

    您创建一个新的 Exception(或 - 更好的 - 特定子类型),其中包含新消息(并将原始异常作为 InnerException 传递)。

    例如。

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

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

    【讨论】:

    • 抛出新异常的问题是在新异常为thrown 的地方抛出了新异常。如果有办法将我的新throw 的堆栈替换为InnerException 的堆栈,或者有一种方法可以追溯地抛出当前异常的来源,那么它将起作用。我已经抛出了我自己的自定义异常,但这是一个临时的解决方法,我需要一个真正的解决方案来解决 (hence the SO question)
    • InnerException 仍然是可检索的,但它保留了原始堆栈跟踪(以及原始异常详细信息的整个上下文,如果您需要其他任何内容)。 .InnerException 只是给你底层的Exception 实例。
    • 异常消息非常具有误导性,特别是在自动翻译成其他语言时,例如 PT-BR。最令人困惑的是 FormatException 一个,它会抛出 A cadeia de caracteres de entrada não estava em um formato correto,或“入站字符串NOT格式不正确”。
    • @EricWu 对于未来,我建议在 .NET 的 github 存储库上创建一个问题:这种错误只有在报告后才会得到修复。
    【解决方案2】:

    处理这种情况的最佳方法是编写适合这种特殊情况的您自己的异常类。然后捕获异常,并抛出你自己的异常。您可能想在这里查看:Exception Constructors -> Exception(String) | MS Docs 了解更多信息。

    如果您觉得自定义异常没有必要,您应该可以在抛出异常时将自定义字符串传递给 Exception 构造函数:Designing Custom Exceptions | MS Docs

    【讨论】:

    • 有没有办法在旧异常的来源处抛出我的新异常,而不是让它追踪到我的throw
    【解决方案3】:

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

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

    【讨论】:

    • 问题在于我的新异常的堆栈跟踪与旧异常的堆栈跟踪不匹配。
    • 应保留内部异常的堆栈跟踪,以便您可以在那里查找。
    【解决方案4】:

    我在a blog post 中找到了解决方案,链接自a news site

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

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

    【讨论】:

    • 理论上,异常类型可能没有带两个参数的 ctor。
    【解决方案5】:

    我只是想在这里插话伊恩与他的回答有关。如果您使用blog post 中的技术,您将不会丢失堆栈。是的,您将丢失最终异常的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
    

    所以你得到了整个堆栈,加上额外的信息

    【讨论】:

      【解决方案6】:

      在我的旧观点中,最好的方法是从 Exception(或其他框架提供的适当的异常类)继承,然后将您的消息字符串放入对以 Message 作为参数的基本构造函数的调用中,就像这样:

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

      【讨论】:

      • 但是你只能在单行/内联中做任何事情。如果您需要做一些额外的工作来弄清楚应该是什么消息,那么您就不走运了。
      【解决方案7】:

      我知道这是一个非常古老的问题,但我认为现有的任何答案都不是理想的(它们在 innerExceptions 或掩码行号中隐藏异常详细信息)。我使用的方法是使用静态工厂方法而不是构造函数来实例化异常:

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

      并将消息修改为您心中的内容。

      【讨论】:

        【解决方案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);
        

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

        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;”无一例外

        【讨论】: