我应该抛出自己的ArgumentOutOfRangeException还是从下面冒出一个泡泡?

时间:2010-03-26 20:14:00

标签: c# .net exception exception-handling

我有一个包含List<>

的类

我通过索引方法获得GetValue:

    public RenderedImageInfo GetValue(int index)
    {
        list[index].LastRetrieved = DateTime.Now;
        return list[index];
    }

如果用户请求的索引超出范围,则会抛出ArgumentOutOfRangeException。

我应该让这件事发生或检查并扔掉我自己的?即。

    public RenderedImageInfo GetValue(int index)
    {
        if (index >= list.Count)
        {
            throw new ArgumentOutOfRangeException("index");
        }
        list[index].LastRetrieved = DateTime.Now;
        return list[index];
    }

在第一个场景中,用户将从内部列表中获得异常,从而打破了用户不需要了解底层对象的OOP目标。

但在第二种情况下,我觉得好像在添加冗余代码。

修改
现在,我想到了,第三种情况,我捕获内部异常,修改它,并重新抛出它?

4 个答案:

答案 0 :(得分:12)

你应该抛出自己的,原因有两个:

  1. 您可以明确设置appropriate parameter name in the constructor。这样,异常具有超出范围的Argument的适当参数信息。
  2. (次要)就您的用户而言,内部列表的异常将具有无效的堆栈跟踪。通过构造一个新的异常,您可以将堆栈跟踪显示为不合适的堆栈跟踪,这将更容易进行用户的调试。
  3. 至于捕获和修改内部异常 - 我不一定会推荐这个。如果异常是额外信息可能有用的异常,则应使用新异常的InnerException来提升此信息。

    在这个例子中(ArgumentOutOfRangeException),存在内部列表的事实应该作为实现细节保留,并且您的用户没有理由看到该信息。

答案 1 :(得分:3)

如果您使用的是.NET 2.0或更高版本,则可以使用the inner exception,如下所示:

public RenderedImageInfo GetValue(int index)
{
    try {
        list[index].LastRetrieved = DateTime.Now;
        return list[index];
    } catch (ArgumentOutOfRangeException ex) {
        throw new ArgumentOutOfRangeException("index", ex);
    }
}

这样,您的用户将主要看到您的例外,但如果您(或他们)想要,可以深入挖掘。

编辑:我现在看到自.NET 1.0以来一直支持InnerException,它只是新构造函数。在2.0之前,我认为没有办法在.NET中为InnerException实际设置ArgumentOutOfRangeException。他们只是忘记了它,还是我上面写的代码与预期用途有关?

答案 2 :(得分:3)

  

...打破m [y]用户不需要知道底层对象的OOP目标。

抛出相同的异常对封装没有任何作用。您的(暗示/记录 - 因为我们没有检查异常或DbC)合同规定您将在这种情况下抛出ArgumentOutOfRange异常。如何生成该异常,以及它的堆栈跟踪看起来与调用者无关。

如果您将内部实现移动到抛出ArgumentOutOfRange异常的内容,那么您需要自己抛出一个来履行合同(或对合同进行重大更改)

堆栈跟踪(和param名称)用于调试 - 不用于编程访问。除非存在一些安全问题,否则不要担心让它们“泄密”。

顺便说一下,包装异常的建议(你可能会想到)来自一个更抽象的场景。如果用户不能IAuthenticationService

,请考虑投出Login

如果同时存在LdapAuthenticationServiceDatabaseAuthenticationService实现,那么您必须同时捕获LdapDirectoryExceptionSqlException以确定登录失败。完成第三个实现后,您还需要添加它的特定异常类型。所有实施者都将其特定异常包装在FailedAuthenticationException中,您只需担心单一类型。

尽管在InnerException中包含原始异常仍然是个好主意,因为它有助于调试。

我希望你能看到这个场景和你的场景之间的区别 - 在你的情况下,你只是抛出相同的异常类型,所以没有区别。

所有这一切,如果它是一个图书馆 - 然后我会检查它并扔掉。但不是因为OOP纯度 - 而是因为它使合同明确,不太可能无意中改变。如果我使用的是[T | B] DD,那么我只是为它编写一个测试,然后让List抛出 - 测试使合同明确。

答案 3 :(得分:2)

在这种情况下让异常泡沫很好。

修改

我被要求更新我的答案,以包括一些回复......

可以说,捕获异常并将其重新抛出为内部更加健壮,因为它可以使您与变更隔离并创建更一致的界面。话虽如此,在开发人员可以访问所有代码的环境中,收益可能非常小 - 无人接近。而且,我们获得的任何微小的好处都不是免费的。额外的错误处理代码增加了复杂性,并且必须对其进行测试以确保其有效。它还增加了在维护期间破坏方法的风险,因为所有防弹措施都可能掩盖方法的基本功能。

那么,值得吗?这取决于。它归结为成本/收益分析,只能针对特定环境进行。对于公共API,例如AlphaFS,其好处可能会超过成本。对于非专为重复使用而设计的代码的内部使用,可能不是。这是我原来答案的背景,我仍然坚持。此外,正如Hightechrider所指出的那样,可能还有其他技术,例如编码合同,它们以更低的成本产生相同的好处,改变方程式,有利于稳健性。

与往常一样,我愿意接受理性的分歧。但是,我建议我们都尽量避免一刀切的想法。

修改

当我说错误处理代码容易出错时,我要说清楚我并不疯狂,看看原来的问题并告诉我你是否能发现错误。如果你这样做,我会提供+1。