如果您不是库开发人员,请不要在代码中采取防御措施
改为编写单元测试
事实上,即使你正在开发一个库,大部分时间都在扔:不好
1.绝对不能在 C# 中对 int 进行测试 null :
它会引发警告 CS4072,因为它总是错误的。
2。抛出异常意味着它是异常的:异常和罕见。
它不应该在生产代码中引发。特别是因为异常堆栈跟踪遍历可能是一项 CPU 密集型任务。而且您永远无法确定异常会在哪里被捕获,如果它被捕获并记录,或者只是简单地被忽略(在杀死一个后台线程之后),因为您不控制用户代码。 c# 中没有 "checked exception"(就像在 java 中一样),这意味着您永远不知道 - 如果没有很好的文档记录 - 给定方法会引发什么异常。顺便说一句,这种文档必须与代码保持同步,这并不总是容易做到的(增加维护成本)。
3.例外情况会增加维护成本。
由于异常是在运行时和特定条件下引发的,它们可能会在开发过程的后期被检测到。您可能已经知道,在开发过程中越晚检测到错误,修复的成本就越高。我什至看到异常引发代码进入生产代码并且一周内没有引发,只是为了以后的每一天都引发(杀死生产。哎呀!)。
4.抛出无效输入意味着您无法控制输入。
图书馆的公共方法就是这种情况。但是,如果您可以在编译时使用另一种类型(例如不可为空的类型,如 int)检查它,那么这就是要走的路。当然,由于他们是公开的,他们有责任检查输入。
想象一下用户使用他认为有效的数据,然后由于副作用,堆栈跟踪深处的一个方法会抛出ArgumentNullException。
- 他会有什么反应?
- 他该如何应对呢?
- 提供解释信息方便吗?
5.私有方法和内部方法绝不应该抛出与其输入相关的异常。
您可能会在代码中抛出异常,因为外部组件(可能是数据库、文件或其他)行为异常,并且您无法保证您的库将继续在当前状态下正确运行。
公开一个方法并不意味着它应该(只是它可以)从你的库外部调用(Look at Public versus Published from Martin Fowler)。使用 IOC、接口、工厂并仅发布用户需要的内容,同时使整个库类可用于单元测试。 (或者你可以使用InternalsVisibleTo机制)。
6.在没有任何解释信息的情况下抛出异常是在取笑用户
无需提醒工具损坏时的感受,也不知道如何修复它。是的,我知道。你来SO问一个问题...
7.无效输入意味着它会破坏您的代码
如果您的代码可以生成带有该值的有效输出,那么它不是无效的,您的代码应该管理它。添加一个单元测试来测试这个值。
8.从用户角度思考:
你喜欢你使用的库抛出异常来砸你的脸吗?比如:“嘿,无效,你应该知道的!”
即使从你的角度来看 - 根据你对图书馆内部的了解,输入是无效的,你如何向用户解释它(友善和礼貌):
- 清晰的文档(在 Xml 文档和架构摘要中可能会有所帮助)。
- 使用库发布 xml 文档。
- 清除异常中的错误说明(如果有)。
- 给出选择:i>
看看 Dictionary 类,你更喜欢什么?你认为什么叫最快?什么调用会引发异常?
Dictionary<string, string> dictionary = new Dictionary<string, string>();
string res;
dictionary.TryGetValue("key", out res);
或
var other = dictionary["key"];
9.为什么不使用Code Contracts?
这是一种避免丑陋的if then throw 并将合约与实现隔离的优雅方式,允许同时将合约重用于不同的实现。您甚至可以将合同发布给您的图书馆用户,以进一步解释他如何使用图书馆。
作为结论,即使您可以轻松使用throw,即使您在使用 .Net Framework 时会遇到异常引发,这并不意味着可以随意使用它。