为什么不建议用 try catch?

2019-11-16 12:17:38 +08:00
 wysnylc

不问是不是,就问为什么。这个问题看来需要从头说起。

一句话解释: try catch 机制非常好。那些觉得 try catch 不行的人,是他们自己的水平有问题,无法理解这种机制。并且这群人写代码不遵守规则,喜欢偷懒,这才造成 try catch 不好的错觉。

详细解释: 1.程序要健壮,必须要设计报错机制。 最古老,也是最常见的,比如: bool CreateFile( ); //如果创建文件失败就返回 false,否则返回 true。 这种报错方式,显然不好。因为它没有给出产生错误的具体原因。

2.改进:一个函数或过程,会因为不同的原因产生错误,报错机制必须要把这些错误原因进行区分后,再汇报。 比如: int CreateFile(): //如果创建成功就返回 1. //如果是因为没有权限,导致失败,返回-1。 //如果是因为文件已经存在,导致失败,返回-2。 //如果是因为创建文件发生超时,导致失败,返回-3。 这样看上去,比 [ 1 ] 要好些,至少指出了比较具体的失败原因,但是,还不够。

3.很多情况下,函数需要把详细的原因,用字符串的方式,返回: class Result { ....int State;//同 [ 2 ] ....string ErrorMessage;//如果失败,这里将给出详细的信息,如果有可能,应该把建议也写上去。 }

Result CreateFile(); //如果创建成功,返回的 Result,State 为 1,ErrorMessage 为 null。 //如果是因为没有权限,导致失败,返回的 Result,State 为-1,ErrorMessage 为"用户 [ guest ] 没有权限在 [ C:] 这个目录下创建该文件。建议您向管理员申请权限,或者更换具有权限的用户。"。 //如果是因为文件已经存在,导致失败,返回的 Result,State 为-2,ErrorMessage 为"文件 [ C:\abc.txt ] 已经存在。如果需要覆盖,请添加参数:arg_overwrite = true"。 //如果是因为创建文件发生超时,导致失败,返回的 Result,State 为-3,ErrorMessage 为"在创建文件时超时,请使用 chkdsk 检查文件系统是否存在问题。"。

4.我个人推崇上面这种方式,完整,美观。但是这种流程,容易与正常的代码混在一起,不好区分开。因此,Java、C#等设计了 try catch 这一种特殊的方式: void CreateFile() //如果创建成功就不会抛出异常。 //如果是因为没有权限,导致失败,会抛出 AccessException,这个 Exception 的 Msg 属性为"用户 [ guest ] 没有权限在 [ C:] 这个目录下创建该文件。建议您向管理员申请权限,或者更换具有权限的用户。"。 //如果是因为文件已经存在,导致失败,会抛出 FileExistedException,这个 Exception 的 Msg 属性为"文件 [ C:\abc.txt ] 已经存在。如果需要覆盖,请添加参数:arg_overwrite = true"。 //如果是因为创建文件发生超时,导致失败,会抛出 TimeoutException,这个 Exception 的 Msg 属性为"在创建文件时超时,请使用 chkdsk 检查文件系统是否存在问题。"。

可见,上述机制,实际上是用不同的 Exception 代替了 [ 3 ] 的 State。

这种机制,在外层使用时: try { ....CreateFile( "C:\abc.txt" ); } catch( AccessException e ) { ....//代码进入这里说明发生 [没有权限错误] } catch( FileExistedException e ) { ....//代码进入这里说明发生 [文件已经存在错误] } catch( TimeoutException e ) { ....//代码进入这里说明发生 [超时错误] } 对比一下 [ 3 ] ,其实这与 [ 3 ] 本质相同,只是写法不同而已。

5.综上,我个人喜欢 [ 3 ] 这类面向过程的写法。但很多喜欢面向对象的朋友,估计更喜欢 [ 4 ] 的写法。然而 [ 3 ] 与 [ 4 ] 都一样。这两种机制都是优秀的错误处理机制。

6.理论说完了,回到正题,题注问:为什么不用 try catch ? 答:这是因为,很多菜鸟,以及新手,他们是这样写代码的: void CreateFile( ) //无论遇到什么错误,就抛一个 Exception,并且也不给出 Msg 信息。 这样的话,在外层只能使用: try { ....CreateFile( "C:\abc.txt" ); } catch( Exception e ) { ....//代码进入这里说明发生错误 } 当出错后,只知道它出错了,并不知道是什么原因导致错误。这同 [ 1 ] 。

以及,即使 CreateFile 是按 [ 4 ] 的规则设计的,但菜鸟在外层是这样使用的: try { ....CreateFile( "C:\abc.txt" ); } catch( Exception e ) { ....//代码进入这里说明发生错误 ....throw Exception( "发生错误" ) } 这种情况下,如果这位菜鸟的同事,调用了这段代码,或者用户看到这个错误信息,也只能知道发生了错误,但并不清楚错误的原因。这与 [ 1 ] 是相同的。

出于这些原因,菜鸟的同事,以及用户,并没有想到,造成这个问题是原因菜鸟的水平太差,写代码图简单省事。他们却以为是 try catch 机制不行。

因此,这就导致了二逼同事,以及傻比用户,不建议用 try catch。 原文地址:https://www.zhihu.com/question/29459586

13249 次点击
所在节点    Java
78 条回复
NOspy
2019-11-16 13:17:53 +08:00
原来是友军。
iwtbauh
2019-11-16 14:22:07 +08:00
3, 4 并不好

因为这种设计将错误处理和本地化这两个无关的模块耦合在了一起。

不应该直接返回人类可读的错误信息。
guyeu
2019-11-16 14:31:51 +08:00
了解一下 go 语言
result, error := func()
if error != nil {
...
}
wysnylc
2019-11-16 15:05:55 +08:00
@iwtbauh #2 不应该直接返回人类可读的错误信息? 应该返回外星人可读的错误信息??????满脑壳的疑问??????????????
@guyeu #3 不同语言不予置评,文中已经说的很明白 try-catch 的作用和意义
qdwang
2019-11-16 15:09:23 +08:00
try catch 没啥好的,真的好的是把错误放到类型里。
wysnylc
2019-11-16 15:20:46 +08:00
@qdwang #5 A 方法只需要返回一个 int 类型的数值,正常用 try-catch 设计不需要改变方法的结构入参返回值该怎么样怎么样.
但是如果把错误放到返回值中,那么 int 肯定是必须改,又因为需要详细的错误 code 和 message 那么此时返回值会变成一个含有 code,message,result(正常结果)的对象或者 map
请问你的项目里到处充斥着这种接口,可读性强吗?美观么?每一个错误都重新定义 code 吗?
wysnylc
2019-11-16 15:21:36 +08:00
@NOspy #1 真的有人把错误信息完完全全的塞在返回值里,没救
zunceng
2019-11-16 15:26:18 +08:00
@guyeu go 也不完美 1.13 上 给出的 Wrap 和 Unwrap 用对了 才能解决题主说的后面几个点

归根结底还是在人
wysnylc
2019-11-16 15:28:11 +08:00
@zunceng #8 go 的异常处理被多少人诟病了,都不是性能问题,是设计问题!
HanMeiM
2019-11-16 15:30:51 +08:00
try catch 挺好的啊,不用把服务层的 false 一路传回应用层,也不用在返回结构体里面定义错误信息
guyeu
2019-11-16 15:37:46 +08:00
@wysnylc #4
@zunceng #8
java 的异常设计优势是让程序的各种无法预期的错误可以不影响代码结构,这一点比楼主提的 2 和 3 都好。go 的设计也是为了这个目标,但是实现得很愚蠢,java 里可以很容易得把异常抛给上层去处理。
Wrap 和 Unwrap 其实很大程度上是 java 的 Cause by 和 Supressed 的翻版。
就现在而言,个人认为 java 的这个异常方案可以说是最接近最优解的了,然而无论在哪里,处理异常只要把握住以下两点就可以了:
1. 不要丢失信息,包括异常树和异常堆栈;
2. 简洁,不要侵犯正常逻辑。
wysnylc
2019-11-16 15:42:05 +08:00
@guyeu #11 我有点觉得这些不用 try-catch 是假 javaer,不用 exception 处理异常流程简直没法写代码,用状态码会污染正常的方法结构不用状态码又没法告知上一层我出异常了,他们怎么写代码的?天天只写 void 的吗?
HongJay
2019-11-16 15:47:57 +08:00
try catch 导致潜在的问题不被暴露出来。我是觉得应该具体情况具体分析,应不应该用。

同意知乎回答里面的一个答主。

只有错误可预知时才用 try。所有不可预知的错误用 try 都是不负责任的写法。
ClericPy
2019-11-16 15:48:49 +08:00
一开始以为讲 LBYL 与 EAFP... 蒙着头看一大坨没有换行的代码...
结果结尾一句

因此,这就导致了二逼同事,以及傻比用户,不建议用 try catch。

差点闪了我的腰啊... 这排版还不如把原文地址放第一行上
kidlj
2019-11-16 15:51:35 +08:00
Go 社区经常引用的一篇文章:try-catch is elegant, but is wrong. 建议了解一下。
wysnylc
2019-11-16 15:53:11 +08:00
@HongJay #13 错误都可预知了还要用 try 干嘛?你没看这个回复的评论吗.
@ClericPy #14 只是方便看原文而已,我放上面你样会跳出来杠"原文都有了你还复制一堆出来干嘛",所以你只是为了杠而已
ClericPy
2019-11-16 15:54:54 +08:00
@wysnylc #16 这不是害我看了几十行不换行代码的理由... 你觉得被杠了那我撤了
wysnylc
2019-11-16 15:57:20 +08:00
@kidlj #15 能不能直接发表你的看法?
scriptB0y
2019-11-16 16:00:29 +08:00
同事喜欢函数体进来就是一个 try { } catch (Exception e) { return null; } ,函数内检查每一个变量是不是 null。还说这是程序的健壮性。。。真是说服不了他。

有一次我写了个 throw Exception,把他吓得要命。
sagaxu
2019-11-16 16:04:43 +08:00
checked exception 和 unchecked exception 还能再打一战

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/620152

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX