Noo*_*ass 46 try-catch throw swift
throws关键字是对Swift最大的误解.考虑以下代码:
func myUsefulFunction() throws
Run Code Online (Sandbox Code Playgroud)
我们无法真正理解它会抛出什么样的错误.我们唯一知道的是它可能会引发一些错误.了解错误可能的唯一方法是查看文档或在运行时检查错误.
但这不是针对斯威夫特的本性吗?Swift具有强大的泛型和类型系统,可以使代码具有表现力,但感觉就好像throws完全相反,因为您无法从查看函数签名中获得有关错误的任何信息.
为什么会这样?或者我错过了重要的事情并误解了这个概念?
Rob*_*ier 30
我是Swift中类型错误的早期支持者.这就是斯威夫特团队让我相信我错了.
强类型错误是脆弱的,可能会导致API演变不佳.如果API承诺只抛出3个错误中的一个,那么当在后来的版本中出现第四个错误情况时,我有一个选择:我以某种方式将它埋在现有的3中,或者我强制每个调用者重写他们的错误处理代码处理它.由于它不是原来的3,它可能不是一个非常常见的条件,这给API施加了很大的压力,不会扩展它们的错误列表,特别是一旦框架长期广泛使用(想想:基金会) ).
当然,对于开放的枚举,我们可以避免这种情况,但是开放的枚举不能实现强类型错误的目标.它基本上是一个无类型错误,因为你仍然需要一个"默认".
您可能仍然会说"至少我知道错误来自开放枚举的地方",但这往往会使事情变得更糟.假设我有一个日志记录系统,它尝试编写并获得IO错误.应该归还什么?斯威夫特没有代数数据类型(我不能说() -> IOError | LoggingError),所以我可能不得不换IOError到LoggingError.IO(IOError)(这迫使每一层都明确地重新包装,你不能有rethrows很频繁).即使它确实有ADT,你真的想要IOError | MemoryError | LoggingError | UnexpectedError | ...吗?一旦你有几层,我就会一层一层地包裹着一些潜在的"根本原因",这些根本原因必须被痛苦地解开才能解决.
你打算怎么处理它?在绝大多数情况下,catch块看起来像什么?
} catch {
logError(error)
return
}
Run Code Online (Sandbox Code Playgroud)
Cocoa程序(即"应用程序")深入挖掘错误的确切根本原因并根据每个精确的情况执行不同的操作是非常罕见的.可能有一两个案例有恢复,其余的是你无论如何都做不了的事情.(这是Java中的一个常见问题,带有检查异常,不仅仅是这样Exception;它不像之前没有人走过这条道路.我喜欢Yegor Bugayenko关于Java中检查异常的论据,它基本上认为他的首选Java实践恰好是Swift解.)
这并不是说没有强类型错误非常有用的情况.但是有两个答案:第一,你可以自由地使用枚举实现强类型错误,并获得相当好的编译器实施.不完美(你仍然需要在switch语句之外的默认catch ,但不在内部),但如果你自己遵循一些约定,那就相当不错了.
其次,如果这个用例变得很重要(并且可能),那么在不破坏需要相当通用错误处理的常见情况的情况下,为这些情况添加强类型错误并不困难.他们只会添加语法:
func something() throws MyError { }
Run Code Online (Sandbox Code Playgroud)
呼叫者必须将其视为强类型.
最后,对于非常有用的强类型错误,Foundation需要抛出它们,因为它是系统中最大的错误生成器.(NSError与处理基金会生成的相比,你有多少经常创建一个从头开始?)这将是对Foundation的大规模改革,并且很难与现有代码和ObjC保持兼容.因此,在解决非常常见的Cocoa问题时,类型错误需要绝对精彩,值得考虑作为默认行为.它不可能只是更好一点(更不用说有上述问题).
所以,这并不是说无类型错误是所有情况下错误处理的100%完美解决方案.但这些论点使我确信这是今天在斯威夫特进行的正确方式.
Jer*_*myP 22
选择是一个刻意的设计决定.
他们不希望你不需要在Objective-C,C++和C#中声明异常抛出的情况,因为这使得调用者必须假定所有函数抛出异常并包含样板来处理可能不会发生的异常,或者只是忽略异常的可能性.这些都不是理想的,第二个使得异常不可用,除了你想要终止程序的情况,因为你无法保证在堆栈被展开时调用堆栈中的每个函数都正确地释放了资源.
另一个极端是你提倡的想法,并且可以声明抛出的每种类型的异常.不幸的是,人们似乎反对这样做的后果,即你有大量的catch块,所以你可以处理每种类型的异常.因此,例如,在Java中,他们会将Exception情况减少到与我们在Swift中相同的情况,或者更糟糕的是,他们使用未经检查的异常,因此您可以完全忽略该问题.GSON库是后一种方法的一个例子.
我们选择使用未经检查的异常来指示解析失败.这主要是因为通常客户端无法从错误的输入中恢复,因此强制它们捕获已检查的异常会导致catch()块中出现草率的代码.
https://github.com/google/gson/blob/master/GsonDesignDocument.md
这是一个非常糟糕的决定."嗨,你不能信任你做自己的错误处理,所以你的应用程序应该崩溃".
就个人而言,我认为斯威夫特在权利方面取得了平衡.您必须处理错误,但您不必编写大量的catch语句来执行此操作.如果他们继续前进,人们就会找到颠覆机制的方法.
设计决策的完整理由是https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst
编辑
似乎有些人对我所说的一些事情有疑问.所以这是一个解释.
程序可能抛出异常有两大类原因.
第二种类型的错误通常不会被捕获,因为它们表明对环境的错误假设可能意味着程序的数据已损坏.我无法安全地继续,所以你必须中止.
第一种类型的错误通常可以恢复,但为了安全恢复,每个堆栈帧必须正确展开,这意味着对应于每个堆栈帧的函数必须知道它调用的函数可能抛出异常并采取步骤确保在抛出异常时一致地清理所有内容,例如,使用finally块或等效内容.如果编译器不支持告诉程序员他们忘记计划异常,程序员就不会总是计划异常,而是编写泄漏资源或使数据处于不一致状态的代码.
gson态度如此令人震惊的原因是因为他们说你无法从解析错误中恢复(实际上,更糟糕的是,他们告诉你你缺乏从解析错误中恢复的技能).断言这是一个荒谬的事情,人们总是试图解析无效的JSON文件.如果有人错误地选择XML文件,我的程序会崩溃是一件好事吗?不,不是.它应该报告问题并要求他们选择不同的文件.
顺便说一下,gson的一个例子就是为什么使用未经检查的异常来解决你可以从中恢复的错误.如果我想从某人选择XML文件中恢复,我需要捕获Java运行时异常,但是哪些异常呢?好吧,我可以查看Gson文档,了解它们是否正确并且是最新的.如果他们已经检查了异常,那么API会告诉我期待哪些异常,编译器会告诉我是否处理它们.
| 归档时间: |
|
| 查看次数: |
3305 次 |
| 最近记录: |