这是否是自定义异常的良好实践?

8
public class PageNotFoundException : HttpException
{
    public PageNotFoundException()
        : base(404, "HTTP/1.1 404 Not Found")
    {

    }
}

这个想法是,不必每次都输入这些内容。
throw new HttpException(404, "HTTP/1.1 404 Not Found") 

我更愿意写作。
throw new PageNotFoundException();

我本来想添加一个重载函数,包括内部异常,但是我从未在try/catch块中使用过这个功能。

你认为这样做是否好的实践?
例如,从异常继承并将硬编码信息传递给base(...)。

3个回答

8

我决定重新回答你的问题,并从更广泛的角度来看待它,因为 MVC 应用程序并不是唯一适用这些最佳实践的东西。

(1) 回答:这不是一个好的实践。你应该使用异常构造器方法来直接抛出 HttpException 异常。

public static void ThrowPageNotFoundException() {
    throw new HttpException((Int32)HttpStatusCode.NotFound, "HTTP/1.1 404 Not Found");
}

(2) DO. 使用异常构建器方法(例如我提供的代码)。这样可以避免使用自己的异常类型所带来的额外性能损耗,并且允许进行内联处理。抛出异常的成员不会被内联处理。这将是方便抛出异常的适当替代方法。

(3) DO. 尽可能使用基类库异常,仅在绝对没有满足需求的基础异常时才创建自定义异常。创建自定义异常会增加更深层次的异常层级,使得调试变得困难,而且还会增加额外的性能开销和代码冗余。

(4) Do NOT. 抛出基类System.Exception。而应该使用特定的异常类型。

(5) Do NOT. 仅仅为了方便就创建自定义异常。这不是自定义异常的好理由,因为异常本质上是很昂贵的。

(6) Do NOT. 只是为了拥有自己的异常类型而创建自定义异常。

(7) Do NOT. 抛出可以通过更改调用代码避免的异常。这表明您的API存在可用性错误,而不是实际问题。

任何阅读过.NET开发系列的Framework Design Guidelines的人都会知道这些做法,它们是非常好的做法。这些正是.NET框架和MVC所建立的基础。


这是一个MVC网站。当请求一个不存在的ID实体时,我会抛出404错误。这有什么问题吗?StackOverflow本身就使用这种方法。试着访问http://stackoverflow.com/questions/1022181121312312331 - Marko
1
异常处理绝非快速之举。抛出异常只在特殊情况下发生——此时内联方法毫无意义。 - zmbq
错误。最常见的错误之一是开发人员经常认为异常仅用于特殊情况。异常处理的性能和优化确实很重要。 - David Anderson
3
很不幸,这个答案被标记为采纳的答案。公共静态方法不是解决这个问题的正确方式。你将静态方法锚定在什么上面?@Tanzelax 推荐使用异常工厂是正确的。此外,“规则”6没有理由是荒谬的。捕获特定的异常类型而无需检查神奇的属性(或更糟糕的是,它们的组合)以派生变体并不是一种不好的做法。 - David Peden

2

如果是你在第一时间抛出了异常,那么可以。但是,如果你捕获了一个HttpException,然后尝试抛出一个PageNotFoundException,那么你应该将原始异常放入InnerException。


如上所述... "我本来想添加一个包含innerException的重载,但我永远不会在try/catch块中使用它。" - Marko

1

虽然这是一个很好的构造,适用于你自己的代码和使用,但需要考虑的一点是,它可能会促进按照惯例编码,当你与其他/新开发人员打交道时,这可能是危险的。

在你自己的库中,如果你始终抛出一个PageNotFoundException,每当应该抛出404 HttpException时,catch (PageNotFoundException)可能更有意义。然而,当你开始使用其他没有你自定义异常的库时,你将错过其他代码抛出的404 HttpExceptions。同样,如果你有其他开发人员在以后的日期贡献(甚至是你自己未来的添加),大多数功能捕获的是PageNotFoundExceptions的考虑可能被忽略,新的404 HttpExceptions可能会在新模块中抛出,同样不会被复制/粘贴的调用代码所捕获。

基本上,像这样的结构增加了工作在项目上所需的适应时间,并且应该以这样的方式处理,使得这个成本最小化(在易于找到的中央共享对象库中足够清晰地显示,而不是已经太杂乱无章)。

另一方面,如果您正在寻求的是工厂模式的好处,集中生成您的HttpExceptions确实具有价值;如果这是您想要实现的目标,那么最好直接采用这种方法(throw ExceptionFactory.NewPageNotFound())。


就像这样考虑。它是一个处理许多不同实体的MVC网站。我们以用户为例。当用户不存在时,我会抛出404未找到异常。这就是上面代码所做的。虽然我同意您关于新约定的看法,但因为它在大多数控制器中广泛存在,所以这个新异常非常清晰。我考虑过采用@David Anderson的方法,但我更喜欢在我的控制器中抛出实际的异常,而不是通过静态方法抛出,因为我以类似的方式抛出其他异常,而且不想为每个异常都有一个静态方法。 - Marko
像所有的约定一样,只有在沟通和维护约定时出现问题才是问题。如果这对你来说不是问题,那么这肯定是一种优雅的方式来维护异常工厂方法,因为正如你所提到的,代码符合你其余的异常抛出代码。 - Tanzelax

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接