C# 我应该把我自己的论点扔出范围例外还是让一个从下面冒出来?

C# 我应该把我自己的论点扔出范围例外还是让一个从下面冒出来?,c#,.net,exception,exception-handling,C#,.net,Exception,Exception Handling,我有一个类包装列表 我使用索引方法获取值: public RenderedImageInfo GetValue(int index) { list[index].LastRetrieved = DateTime.Now; return list[index]; } 如果用户请求的索引超出范围,这将抛出ArgumentOutOfRangeException 我应该让这一切发生还是检查一下,然后扔掉我自己的?i、 e public R

我有一个类包装列表

我使用索引方法获取值:

    public RenderedImageInfo GetValue(int index)
    {
        list[index].LastRetrieved = DateTime.Now;
        return list[index];
    }
如果用户请求的索引超出范围,这将抛出ArgumentOutOfRangeException

我应该让这一切发生还是检查一下,然后扔掉我自己的?i、 e

    public RenderedImageInfo GetValue(int index)
    {
        if (index >= list.Count)
        {
            throw new ArgumentOutOfRangeException("index");
        }
        list[index].LastRetrieved = DateTime.Now;
        return list[index];
    }
在第一个场景中,用户将有一个来自内部列表的异常,这打破了我的OOP目标,即用户不需要知道底层对象

但在第二个场景中,我感觉好像在添加冗余代码

编辑:

现在我想起来了,第三种情况怎么样,我捕获内部异常,修改它,然后重新显示它?

在这种情况下,让异常冒泡出来是可以的

编辑

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

可以说,捕获异常并将其作为内部重新引用更为健壮,因为它将您与更改隔离开来,并创建一个更一致的接口。话虽如此,在开发人员可以访问所有代码的环境中,好处可能非常小——几乎没有。此外,我们获得的任何微小利益都不是免费的。额外的错误处理代码增加了复杂性,必须对其本身进行测试以确保其正常工作。它还增加了在维护期间破坏该方法的风险,因为该方法的基本功能可能会被所有防弹措施所掩盖

那么,这值得吗?视情况而定。它归结为成本/收益分析,这只能针对特定的环境进行。对于公共API(如AlphaFS),其好处可能远远超过成本。对于不是为显著重用而设计的代码的内部使用,它可能不是。这就是我最初回答的背景,我仍然支持。此外,正如Hightechrider所指出的,可能还有其他技术,比如编码契约,它们产生了几乎相同的好处,但成本较低,改变了等式,有利于健壮性

和往常一样,我对合理的分歧持开放态度。然而,我建议大家尽量避免一刀切的想法

编辑


为了清楚地表明,当我说错误处理代码容易出错时,我并没有发疯,请看一下原始问题,并告诉我您是否能够发现错误。如果你愿意的话,我会给你一个+1。

你应该放弃你自己的,原因有两个:

  • 您可以显式地设置。这样,对于超出范围的参数,异常具有适当的参数信息
  • (次要)就用户而言,内部列表的异常将具有无效的堆栈跟踪。通过构造一个新的异常,您可以获得堆栈跟踪,该跟踪显示您的方法是不合适的,这将更便于用户调试
  • 至于捕获和修改内部异常,我不推荐这样做。如果异常是额外信息可能有用的异常,则应使用新异常的名称来传播此信息


    在本例(ArgumentOutOfRangeException)中,存在内部列表这一事实应作为实现细节保留,用户没有理由查看该信息。

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

    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以来,它就一直受到支持,只是这个构造函数是新的。在2.0之前的.NET中,我看不到为
    ArgumentOutOfRangeException
    实际设置
    InnerException
    的方法。是他们忘记了,还是我上面写的代码违背了预期用途

    …打破了用户不需要了解底层对象的m[y]OOP目标

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

    如果您将内部实现移动到不会抛出ArgumentOutOfRange异常的位置,那么您需要自己抛出一个异常来履行合同(或者对合同进行破坏性更改)

    堆栈跟踪(和参数名)用于guy调试,而不是用于编程访问。除非出于安全考虑,否则不必担心让它们“泄漏”

    顺便说一句,包装异常的建议(您可能正在考虑)来自一个更抽象的场景。考虑如果用户不能<代码>登录,

    ,将引发“<代码> IudioTestService < /代码>”。 如果同时存在
    LdapAuthenticationService
    DatabaseAuthenticationService
    实现,那么您必须捕获
    LdapDirectoryException
    SqlException
    以确定登录失败。当第三个实现完成时,您还需要添加它的特定异常类型。所有实现者都将其特定异常包装在一个
    FailedAuthenticationException
    中,您只需要担心一种类型

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

    我希望您能看到这个场景和您的场景之间的区别——在您的例子中,您只是抛出了相同的异常类型,所以没有区别

    说了这么多,如果是图书馆的话,我会检查一下然后扔掉。但这并不是为了OOP的纯洁性,而是因为它使契约变得明确,不太可能在无意中被更改。如果我使用的是[T | B]DD,那么我只需要为它编写一个测试,然后让
    Lis