.NET 库的异常处理模式 - 处理、无人值守或重新抛出?
Exceptions handling patterns for .NET library - handle, leave unattended or rethrow?
我正在编写一个 C# 库,其 DLL
稍后将被其他应用程序的代码使用。
当库中有可能引发异常的语句时,例如 System.IO.IOException
,我应该立即捕获它还是应该让它被使用的应用程序的程序捕获是吗?
我问这个是因为我没有在我的库中捕获异常。在调试我的应用程序(使用该库)时,Visual Studio 在库中将其显示为 未处理的异常 。但是当我 > Continue 时,程序继续运行并且异常被应用程序捕获。它似乎工作正常。
现在,这样做可以吗?也就是说,部署应用程序时会不会出现任何问题?或者它应该完全被图书馆本身抓住吗?或者 MSDN 是否在某处建议了比这更好的 "best practice"?
编写好的库是一门艺术和科学。假设您已经决定提供的功能并将相关 API 公开给库的使用者,那么似乎有一个问题仍未解决 - 异常处理。
一般来说,在库代码执行过程中引发的异常可以分为两组:
1. 依赖于库的实现和功能。
2. 取决于消费者或系统,超出您图书馆的兴趣范围。
恕我直言,最佳做法是定义库代码抛出的所有异常,并从 ApplicationException
或更好的是从相关的 .NET 异常中派生出你的 lib 特定前缀,即:<LibShortName>FormatException
或 <LibShortName>InvalidOperationException
。然后,如果您的库应该向客户端代码抛出异常,请使用库派生的异常。
如果在库外引发了异常,并且从您的库功能角度来看它们是有意义的,您可以捕获它们并抛出新的库异常,其中包含作为内部异常的已经捕获的异常,或者使用您的 <LibShortName>AggregateException
创建在将异常暴露给客户之前链接异常。
以上方案用于许多流行的 .NET 库,即 SharpSkia。还有其他方法可以解决这个问题,但是,我发现上面的方法对图书馆消费者来说最有用——他们会得到尽可能多的关于错误的信息。此外,他们可以轻松地使用基于类型的模式来捕获和处理异常。
编写好的库最重要的方面之一就是性能。要获得最适合您的库,请不要在您的代码中使用异常来控制流程。异常应该专门用于错误条件。违反上述模式的糟糕设计的一个很好的例子是 ANTLR C# parser framewrok
AFAIR v2。在某个阶段,库使用异常来控制效率极低的解析逻辑。后来它被重构为仅针对错误情况使用异常,从而将性能提高了几个数量级。
关于设计 .NET 库 API 的最佳书籍之一是:
- Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries 作者:Krzysztof Cwalina 和 Brad Adams。
阅读这些内容可能会很有趣,这些是 API Reviews
在 .NET Core corefx repository and a bit old but still a classic one Krzysztof Cwalina blog posts tagged with General API Design
的封闭式和开放式问题中的讨论
我正在编写一个 C# 库,其 DLL
稍后将被其他应用程序的代码使用。
当库中有可能引发异常的语句时,例如 System.IO.IOException
,我应该立即捕获它还是应该让它被使用的应用程序的程序捕获是吗?
我问这个是因为我没有在我的库中捕获异常。在调试我的应用程序(使用该库)时,Visual Studio 在库中将其显示为 未处理的异常 。但是当我 > Continue 时,程序继续运行并且异常被应用程序捕获。它似乎工作正常。
现在,这样做可以吗?也就是说,部署应用程序时会不会出现任何问题?或者它应该完全被图书馆本身抓住吗?或者 MSDN 是否在某处建议了比这更好的 "best practice"?
编写好的库是一门艺术和科学。假设您已经决定提供的功能并将相关 API 公开给库的使用者,那么似乎有一个问题仍未解决 - 异常处理。
一般来说,在库代码执行过程中引发的异常可以分为两组: 1. 依赖于库的实现和功能。 2. 取决于消费者或系统,超出您图书馆的兴趣范围。
恕我直言,最佳做法是定义库代码抛出的所有异常,并从 ApplicationException
或更好的是从相关的 .NET 异常中派生出你的 lib 特定前缀,即:<LibShortName>FormatException
或 <LibShortName>InvalidOperationException
。然后,如果您的库应该向客户端代码抛出异常,请使用库派生的异常。
如果在库外引发了异常,并且从您的库功能角度来看它们是有意义的,您可以捕获它们并抛出新的库异常,其中包含作为内部异常的已经捕获的异常,或者使用您的 <LibShortName>AggregateException
创建在将异常暴露给客户之前链接异常。
以上方案用于许多流行的 .NET 库,即 SharpSkia。还有其他方法可以解决这个问题,但是,我发现上面的方法对图书馆消费者来说最有用——他们会得到尽可能多的关于错误的信息。此外,他们可以轻松地使用基于类型的模式来捕获和处理异常。
编写好的库最重要的方面之一就是性能。要获得最适合您的库,请不要在您的代码中使用异常来控制流程。异常应该专门用于错误条件。违反上述模式的糟糕设计的一个很好的例子是 ANTLR C# parser framewrok
AFAIR v2。在某个阶段,库使用异常来控制效率极低的解析逻辑。后来它被重构为仅针对错误情况使用异常,从而将性能提高了几个数量级。
关于设计 .NET 库 API 的最佳书籍之一是:
- Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries 作者:Krzysztof Cwalina 和 Brad Adams。
阅读这些内容可能会很有趣,这些是 API Reviews
在 .NET Core corefx repository and a bit old but still a classic one Krzysztof Cwalina blog posts tagged with General API Design