joe*_*moe 87 error-handling exception-handling exception error-reporting
Google的Go语言作为一种设计选择没有例外,Linux的Linus称之为例外废话.为什么?
asv*_*kau 75
异常使得编写代码非常容易,抛出异常会破坏不变量并使对象处于不一致状态.它们基本上迫使你记住你所做的大多数声明都可能抛出,并正确处理它.这样做可能很棘手且反直觉.
考虑这样的事情作为一个简单的例子:
class Frobber
{
int m_NumberOfFrobs;
FrobManager m_FrobManager;
public:
void Frob()
{
m_NumberOfFrobs++;
m_FrobManager.HandleFrob(new FrobObject());
}
};
Run Code Online (Sandbox Code Playgroud)
假设FrobManager
会delete
的FrobObject
,这看起来不错,对吧?或许不是......想象一下,如果是FrobManager::HandleFrob()
或者operator new
抛出异常.在此示例中,m_NumberOfFrobs
不会回滚增量.因此,任何使用此实例的人Frobber
都会有一个可能已损坏的对象.
这个例子可能看起来很愚蠢(好吧,我不得不伸张自己一点来构造一个:-)),但是,如果程序员不是经常考虑异常,并确保每个状态的排列都滚动,那么它就是一个例子.只要有投掷,你就会以这种方式陷入困境.
举个例子,你可以把它想象成你想到的互斥体.在一个关键部分中,您依赖于几个语句来确保数据结构不会被破坏,并且其他线程无法看到您的中间值.如果这些陈述中的任何一个只是随机地不运行,那么你最终会陷入痛苦的世界.现在拿掉锁和并发,并考虑每个方法.如果愿意,可以将每个方法视为对象状态的排列事务.在方法调用开始时,对象应该是干净状态,最后还应该是一个干净的状态.在这两者之间,变量foo
可能与之不一致bar
,但您的代码最终会纠正它.什么例外意味着你的任何一个陈述都可以随时打断你.在每个单独的方法中你有责任让它正确并在发生这种情况时回滚,或者命令你的操作使得抛出不会影响对象状态.如果你弄错了(很容易犯这种错误),那么调用者最终会看到你的中间值.
像CII程序员喜欢提到的RAII这样的方法作为解决这个问题的最终解决方案,可以很好地防止这种情况发生.但它们不是一颗银弹.它将确保您在throw上释放资源,但不会让您不必考虑对象状态和调用者看到中间值的损坏.因此,对于很多人来说,通过编码风格的命令,更容易说,没有例外.如果限制您编写的代码类型,则更难引入这些错误.如果不这样做,那么犯错很容易.
已经编写了关于C++中异常安全编码的全书.很多专家都弄错了.如果它真的那么复杂并且有如此多的细微差别,那么这可能是你需要忽略该功能的一个好兆头.:-)
Ste*_*n C 49
Go语言设计常见问题中解释了Go没有异常的原因:
例外是一个类似的故事.已经提出了许多异常设计,但每种设计都增加了语言和运行时的复杂性.就其本质而言,例外跨越功能,甚至可能是goroutines; 它们具有广泛的影响.人们还担心它们会对图书馆产生什么影响.根据定义,它们与支持它们的其他语言相比具有特殊的经验,表明它们对库和接口规范有深远的影响.很高兴找到一种设计,使它们真正卓越,而不会鼓励常见错误转变为需要每个程序员进行补偿的特殊控制流程.
与泛型一样,例外仍然是一个悬而未决的问题.
换句话说,他们还没有想出如何以他们认为令人满意的方式支持Go中的例外.他们并不是说例外本身就是坏事;
更新 - 2012年5月
Go设计师现在已经爬下篱笆.他们的FAQ现在说:
我们认为将异常耦合到控制结构(如try-catch-finally惯用法)会导致代码复杂化.它还倾向于鼓励程序员标记太多普通错误,例如未能打开文件,这是特殊的.
Go采用不同的方法.对于简单的错误处理,Go的多值返回使得报告错误变得容易,而不会使返回值超载.规范错误类型与Go的其他功能相结合,使错误处理变得愉快,但与其他语言完全不同.
Go还有一些内置功能,可以在真正特殊的条件下发出信号并从中恢复.恢复机制仅作为函数状态在错误发生后被拆除的一部分执行,这足以处理灾难但不需要额外的控制结构,并且如果使用得当,可以产生干净的错误处理代码.
有关详细信息,请参阅Defer,Panic和Recover文章.
所以简短的回答是他们可以使用多值返回来做不同的事情.(而且他们确实有一种异常处理形式.)
...... Linux的Linus称之为异常废话.
如果你想知道为什么Linus认为异常是垃圾,最好的办法是找他关于这个主题的着作.到目前为止,我唯一跟踪的是这句引言,它嵌入了几个关于C++的电子邮件中:
"整个C++异常处理事情从根本上被破坏了.对内核来说尤其破坏了."
你会注意到他特别谈论C++异常,而不是一般的异常.(和C++异常也显然有一些问题,使他们棘手的正确使用.)
我的结论是,Linus根本没有调用异常(总的来说)"废话"!
Rob*_*vey 29
例外情况本身并不坏,但如果你知道它们会发生很多事情,那么它们在性能方面可能会很昂贵.
经验法则是异常应该标记异常条件,并且不应该使用它们来控制程序流.
Tru*_*ill 25
我不同意"只在异常情况下抛出异常".虽然一般都是正确的,但它具有误导性.例外是错误条件(执行失败).
无论您使用哪种语言,都可以获取一份" 框架设计指南:可重用.NET库的约定,惯用法和模式"(第2版).关于异常抛出的章节是没有同行的.第一版的一些引用(我工作的第二版):
有关于异常的好处的页面说明(API一致性,错误处理代码的位置选择,改进的健壮性等).有一个关于性能的部分包括几种模式(Tester-Doer,Try-Parse).
异常和异常处理是不坏.像任何其他功能一样,它们可能被滥用.
dda*_*daa 11
从golang的角度来看,我想没有异常处理可以使编译过程简单安全.
从Linus的角度来看,我理解内核代码是关于极端情况的ALL.所以拒绝异常是有道理的.
如果将当前任务放在地板上,并且常见案例代码比错误处理更重要,那么代码中的例外是有意义的.但它们需要从编译器生成代码.
例如,它们适用于大多数高级用户代码,例如Web和桌面应用程序代码.
小智 11
它本身的例外情况并非"坏",而是有时处理异常的方式往往是坏的.在处理异常时可以应用几个指南来帮助缓解其中的一些问题.其中一些包括(但肯定不限于):
典型的论点是,无法判断特定代码片段(取决于语言)会产生什么异常,并且它们太像goto
s,因此难以在精神上跟踪执行情况.
http://www.joelonsoftware.com/items/2003/10/13.html
在这个问题上肯定没有达成共识.我想说从像Linus这样的核心C程序员的角度来看,异常绝对是一个坏主意.但是,典型的Java程序员处于截然不同的情况.
小智 7
例外也不错.它们与C++的RAII模型很吻合,这是关于C++最优雅的东西.如果你已经拥有一堆并非异常安全的代码,那么它们在这种情况下就不好了.如果你正在编写真正的低级软件,比如Linux操作系统,那么它们就很糟糕.如果您喜欢通过一堆错误返回检查来乱丢代码,那么它们就无济于事了.如果在抛出异常(C++析构函数提供)时没有资源控制计划,那么它们就很糟糕.
因此,异常的一个很好的用例是......
假设您在一个项目中,每个控制器(大约 20 个不同的主要控制器)都使用操作方法扩展单个超类控制器。然后,每个控制器都会执行一堆彼此不同的操作,在一种情况下调用对象 B、C、D,在另一种情况下调用对象 F、G、D。在许多情况下,异常会发挥作用,因为有大量的返回代码,并且每个控制器都以不同的方式处理它。我修改了所有代码,从“D”抛出正确的异常,在超类控制器操作方法中捕获它,现在我们所有的控制器都是一致的。以前,D 对于多个不同的错误情况返回 null,我们想告诉最终用户但不能,而且我不想将 StreamResponse 变成令人讨厌的 ErrorOrStreamResponse 对象(在我看来,将数据结构与错误混合在一起是一股难闻的味道,我看到很多代码返回一个“流”或其他类型的实体,其中嵌入了错误信息(它实际上应该是函数返回成功结构或我可以使用异常与返回代码执行的错误结构)....虽然我有时可能会考虑使用 C# 的多重响应方式,但在很多情况下,异常可以跳过很多层(我不需要清理资源的层)。
是的,我们必须担心每个级别和任何资源清理/泄漏,但总的来说,我们的控制器都没有任何资源需要清理。
感谢上帝,我们有例外,否则我会进行巨大的重构,并在本应是简单编程问题的问题上浪费太多时间。
归档时间: |
|
查看次数: |
29127 次 |
最近记录: |