FormattedException而不是在.NET中抛出新的Exception(string.Format(...))

时间:2010-03-31 14:18:45

标签: c# exception architecture

我一直在考虑一个很好的通用异常对象来替换throw new Exception(string.Format("...",...)),以简化和加速这些对象。慢String.Format()格式应该延迟,直到调用Message属性。序列化也有些风险。此外,此类对象可以在以后实现本地化。

更新:此异常应由更具体的用户异常继承,而不是自行抛出。很抱歉没有说清楚。

这就是我想出的。如果有任何改进方法,请评论。谢谢!

/// <summary>
/// Generic exception capable of delayed message formatting.
/// Inherit for more specific exceptions.
/// </summary>
[Serializable]
public class FormattedException : Exception
{
    private readonly object[] _arguments;
    private readonly string _formatStr;
    private readonly bool _useFormat;

    private FormattedException(bool useFormat, Exception inner, string message, params object[] args)
        : base(message, inner)
    {
        _useFormat = useFormat;
        _formatStr = message;
        _arguments = args;
    }

    public FormattedException()
        : this(false, null, null, null)
    {}

    public FormattedException(string message)
        : this(false, null, message, null)
    {}

    public FormattedException(string message, params object[] args)
        : this(true, null, message, args)
    {}

    public FormattedException(Exception inner, string message)
        : this(false, inner, message, null)
    {}

    public FormattedException(Exception inner, string message, params object[] args)
        : this(true, inner, message, args)
    {}

    public override string Message
    {
        get
        {
            if (!_useFormat)
                return _formatStr;

            try
            {
                return string.Format(_formatStr, _arguments);
            }
            catch (Exception ex)
            {
                var sb = new StringBuilder();

                sb.Append("Error formatting exception: ");
                sb.Append(ex.Message);
                sb.Append("\nFormat string: ");
                sb.Append(_formatStr);
                if (_arguments != null && _arguments.Length > 0)
                {
                    sb.Append("\nArguments: ");
                    for (int i = 0; i < _arguments.Length; i++)
                    {
                        if (i > 0) sb.Append(", ");
                        try
                        {
                            sb.Append(_arguments[i]);
                        }
                        catch (Exception ex2)
                        {
                            sb.AppendFormat("(Argument #{0} cannot be shown: {1})", i, ex2.Message);
                        }
                    }
                }

                return sb.ToString();
            }
        }
    }

    #region Serialization

    private const string SerializationField = "FormatString";

    protected FormattedException(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
        _formatStr = (string) info.GetValue(SerializationField, typeof (string));
        // Leave other values at their default
    }

    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);
        // To avoid any serialization issues with param objects, format message now
        info.AddValue(SerializationField, Message, typeof (string));
    }

    #endregion
}

5 个答案:

答案 0 :(得分:3)

这是一个有趣的想法,但不是一个好主意。创建自定义异常的原因与易用性无关 - 只有在有人要捕获该异常类型并执行与其不同的操作时才创建自​​定义异常。

您可以创建扩展方法,而不是自定义异常。

答案 1 :(得分:2)

这是IMO,不是一个很好的设计。

只是看看FormattedException(useFormat)的第一个参数味道不好。如果 它不是格式化的异常(useFormat = false),为什么我使用FormattedException?这显示了糟糕的设计。这基本上导致了这一点:你滥用继承。

您使用继承作为UTILS类的形式,或者某种方式在许多类中具有许多常见且简单的功能。这应该可以帮助你获得DRY方法,除了我觉得它不是一个非常好的方法。这种关系是否代表“IS A”关系?我认为不是。我认为这表示“有一个”关系,这意味着该类具有创建格式化异常的能力,而不是“是一个”格式化的异常。

也许界面更合适?也许装饰图案?也许别的什么?您是否考虑过继承之外的其他解决方案?

IMO您需要的是一个简单的工厂(可能使用扩展方法)...它会返回您提供的任何类型的异常。

当然这只是我的0.02美分,所以我可能会完全离开。

哦,顺便说一句,那些尝试/捕捉异常的内容让我的眼睛流血。

答案 2 :(得分:1)

我有一个类似的类,我已经使用了好几年,也叫做FormattedException。您应该做的一件事是使类抽象,以便必须由使用扩展格式构造函数重载的异常类继承。我不担心内部使用String.Format()的性能影响,因为一旦抛出异常,将对整体应用程序性能产生更大的影响,并且对String.Format()的单个调用的性能影响微不足道。我同意你的整体方法来简化用于使用格式化字符串消息抛出异常的代码,我经常发现自己嵌入了对String.Format()的调用,用于我抛出的许多异常的消息参数。

我把我一直使用的FormattedException版本的项目和NuGet包汇总在一起,并在GitHub和NuGet Gallery上发布。

FormattedException source on GutHub

FormattedException NuGet package

答案 3 :(得分:0)

你不应该序列化参数吗?

答案 4 :(得分:0)

异常处理策略基于异常类型而非异常消息。如果您希望用户处理您的异常,您应该为它添加有意义的语义。但我真的不明白我如何处理你的异常(为用户展示它的经历)。

我认为在Message属性中计算消息并不是一个好主意(对它的多次访问可能会导致显着的性能缺陷)。并且你的所有财产都应该像字段一样(here我谈了一下它。)