You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从.NET的ArgumentException中获取原始未被追加修改的错误消息

如何获取ArgumentException中未拼接参数名的原始消息

当在.NET中编写内部处理类时,我经常使用ArgumentException标识传入数据存在问题、无法完成处理。受程序属性影响,我写入这些异常的文本有时和用户相关,因此经常需要展示在UI界面上。

但我发现ArgumentException会专门重写Message属性,自动追加一段标注触发异常的参数名的字符串。我不希望这些额外文本污染原始消息,因为参数名属于内部处理信息,完全不需要展示给用户,而且它追加的换行、本地化内容会破坏我在UI上展示的消息格式。目前我能想到的规避方法只有不给异常传入真实参数名,但我也不想删除这些信息影响后续调试和日志排查。

当然我可以使用自定义异常类,但这些方法大多用于老DOS游戏专有文件格式的压缩和解压缩,我希望这些方法既可以在wiki上公开文档,也能方便其他开发者直接使用,因此更希望保持其可移植性,避免依赖其他外部类。另外补充说明,子类化ArgumentException同样会遇到相同问题。

ArgumentException中Message属性的底层实现源码如下:

public override String Message
{
    get {
        String s = base.Message;
        if (!String.IsNullOrEmpty(m_paramName)) {
            String resourceString = Environment.GetResourceString("Arg_ParamName_Name", m_paramName);
            return s + Environment.NewLine + resourceString;
        }
        else
            return s;
    }
}

由于Message属性被重写,似乎没有常规方法可以获取内部存储的真实原始消息。按换行拆分的方式十分混乱,且受本地化差异影响可靠性很低(我传入的原始消息本身也可能包含换行),而使用反射的实现方式也不够优雅。是否有简洁的方案可以取回原始消息?


解决方案

这里提供一个兼顾优雅性和可移植性的实现方案:通过封装反射逻辑为扩展方法,对外暴露无感知的原始消息获取接口,同时利用字段缓存避免重复反射带来的性能损耗。

using System;
using System.Reflection;

public static class ArgumentExceptionExtensions
{
    // 缓存Exception类的_message字段反射信息
    private static readonly FieldInfo _exceptionMessageField = typeof(Exception)
        .GetField("_message", BindingFlags.Instance | BindingFlags.NonPublic);

    /// <summary>
    /// 获取ArgumentException中未拼接参数名的原始消息内容
    /// </summary>
    /// <param name="ex">目标ArgumentException对象</param>
    /// <returns>未追加参数名的原始错误消息</returns>
    /// <exception cref="ArgumentNullException">传入的异常对象为空时抛出</exception>
    /// <exception cref="NotSupportedException">无法读取底层消息字段时抛出</exception>
    public static string GetRawMessage(this ArgumentException ex)
    {
        if (ex == null)
            throw new ArgumentNullException(nameof(ex));
        if (_exceptionMessageField == null)
            throw new NotSupportedException("无法在当前运行时中获取Exception的原始消息字段。");

        return _exceptionMessageField.GetValue(ex) as string ?? string.Empty;
    }
}

使用方式非常简单,只需要在捕获到ArgumentException后直接调用扩展方法即可:

try
{
    // 可能抛出ArgumentException的逻辑
}
catch (ArgumentException ex)
{
    // 直接获取到未拼接参数名的原始消息,可直接用于UI展示
    string userFriendlyMessage = ex.GetRawMessage();
    // 日志记录时仍然可以使用ex.Message和ex.ParamName保留完整调试信息
    Logger.LogError(ex, "参数校验失败");
}

该方案满足以下需求:

  • 保留了ArgumentException的ParamName属性,不会影响日志排查和调试
  • 无需自定义异常类,无额外外部依赖,可直接复制到任意.NET项目中使用
  • 仅在首次调用时执行一次反射,后续调用性能损耗可忽略不计
  • 不受系统本地化差异影响,也不会被原始消息中的换行干扰

内容的提问来源于stack exchange,提问作者Nyerguds

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 03:57:02