何时用异常,何时不用,这个问题一直萦绕在我心头。直到看到这个视频,答案不能再清晰了。
https://www.youtube.com/watch?v=UsrHQAzSXkAwww.youtube.com关键点虽然只有我截下来这一屏,所以还是需要我再展开说明一下。引用我以前的问题
一般来说写代码时函数不能足前条件和后条件时抛出异常很合理。但在设计函数时,是扩大前条件,当不满足时抛异常,还是减小前条件,尽量不抛异常?
像返回错误码,或expect一样的接口,可以认为后条件已经包括所有情况,理论上也可以不抛出异常。
从调用方来分析,入参范围应该尽量小,编译时能检查最好;出参应该在保持状态一致的情况下,尽可能范围大,包括一些不完全出错的情况,比如返回空容器,返回哨兵等,从而少抛异常。但不应把出错情况都定义到返回值中,那就把功能接口定义成了检查接口。
部分功能本身就有尝试的情况,那么就可以通过重载,把这种情况从正常的情况中区分处理,返回失败供检查。类似的接口如std::filesystem
有了问题,答案就很明确了。
如果函数不能满足前条件,断言失败。不要扩大前条件,增加需要检查的范围。也不要减少前条件,让调用方做过多的准备工作。满足前条件是调用方的责任,是bug,要修。
函数的后条件应该包括错误的情况,返回错误码和异常都可以,都满足后条件。通过异常或错误码,把错误情况和正常情况中区分出来。如果抛异常,那么不再需要返回错误码,即可以简单的省去返回值中错误值的部分。有返回错误码又抛异常的函数行为有错误。
函数应该总是满足后条件,当且仅当,返回成功时,才可能出现不满足后的情况。这种情况是函数自身的bug,要修。调用方可以放心地断言调用结果满足后条件。
这个解释其实倾向于使用异常。因为按这个解析,前条件由调用方保证,必须在调用之前检查参数有效性,错误处理代码会与正常路径混在一起,非常不好看。