Swift do-try-catch 语法
Swift do-try-catch syntax
我试着理解 swift 中新的错误处理方式 2. 这是我所做的:我首先声明了一个错误枚举:
enum SandwichError: ErrorType {
case NotMe
case DoItYourself
}
然后我声明了一个抛出错误的方法(伙计们,这不是异常。这是一个错误。)。这是该方法:
func makeMeSandwich(names: [String: String]) throws -> String {
guard let sandwich = names["sandwich"] else {
throw SandwichError.NotMe
}
return sandwich
}
问题出在调用方。下面是调用此方法的代码:
let kitchen = ["sandwich": "ready", "breakfeast": "not ready"]
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
}
在 do
行之后,编译器说 Errors thrown from here are not handled because the enclosing catch is not exhaustive
。但在我看来它是详尽无遗的,因为 SandwichError
枚举中只有两种情况。
对于常规的 switch 语句 swift 可以理解它在处理每种情况时都是详尽无遗的。
Swift 担心您的 case 语句没有涵盖所有情况,要修复它您需要创建一个默认 case:
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
} catch Default {
print("Another Error")
}
Swift2 错误处理模型有两个要点:详尽性和弹性。它们一起归结为您的 do
/catch
语句需要捕获所有可能的错误,而不仅仅是您知道可以抛出的错误。
请注意,您没有声明函数可以抛出什么类型的错误,只声明它是否抛出。这是一个零一无穷大的问题:作为为其他人(包括你未来的自己)定义一个函数的人,你不想让你的函数的每个客户适应你的实现中的每一个变化函数,包括它可以抛出的错误。您希望调用函数的代码能够适应这种变化。
因为你的函数不能说明它抛出什么类型的错误(或者将来可能抛出),捕获它错误的 catch
块不知道它可能抛出什么类型的错误。因此,除了处理您知道的错误类型外,您还需要使用通用 catch
语句处理您不知道的错误类型——这样,如果您的函数更改了它在未来抛出的错误集,调用者仍然会捕获它的错误。
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
} catch let error {
print(error.localizedDescription)
}
但我们不止于此。多想想这个弹性的想法。按照你设计三明治的方式,你必须在你使用它们的每个地方描述错误。这意味着无论何时更改错误案例集,都必须更改使用它们的每个地方......不是很有趣。
定义你自己的错误类型背后的想法是让你集中这样的事情。您可以为错误定义一个 description
方法:
extension SandwichError: CustomStringConvertible {
var description: String {
switch self {
case NotMe: return "Not me error"
case DoItYourself: return "Try sudo"
}
}
}
然后你的错误处理代码可以要求你的错误类型来描述它自己——现在你处理错误的每个地方都可以使用相同的代码,并且也可以处理未来可能出现的错误情况。
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch let error as SandwichError {
print(error.description)
} catch {
print("i dunno")
}
这也为错误类型(或它们的扩展)支持其他报告错误的方式铺平了道路——例如,您可以在错误类型上有一个知道如何呈现 UIAlertController
用于向 iOS 用户报告错误。
我怀疑这还没有正确实施。 Swift Programming Guide 似乎绝对意味着编译器可以推断出详尽的匹配项 'like a switch statement'。它没有提到需要一个通用的 catch
才能详尽无遗。
您还会注意到错误在 try
行,而不是块的末尾,即在某些时候,编译器将能够查明哪个 try
语句在块具有未处理的异常类型。
虽然文档有点模棱两可。我浏览了“Swift 中的新功能”视频,但找不到任何线索;我会继续努力的。
更新:
我们现在已经到了 Beta 3,没有任何 ErrorType 推断的提示。我现在相信,如果这是曾经计划过的(我仍然认为它在某个时候是这样),协议扩展的动态调度可能会扼杀它。
Beta 4 更新:
Xcode 7b4 添加了对 Throws:
的文档注释支持,“应该用于记录可以抛出的错误以及原因”。我想这至少提供了 一些 机制来将错误传达给 API 消费者。有了文档,谁还需要类型系统!
另一个更新:
在花了一些时间希望自动 ErrorType
推理并弄清楚该模型的局限性之后,我改变了主意 - this 是我希望 Apple 实现的.本质上:
// allow us to do this:
func myFunction() throws -> Int
// or this:
func myFunction() throws CustomError -> Int
// but not this:
func myFunction() throws CustomErrorOne, CustomErrorTwo -> Int
又一个更新
Apple 的错误处理原理现在可用 here. There have also been some interesting discussions on the swift-evolution 邮件列表。本质上,John McCall 反对类型化错误,因为他相信大多数库最终都会包含一般性错误情况,并且类型化错误不太可能在样板代码之外增加太多代码(他使用术语 'aspirational bluff') . Chris Lattner 表示,如果 Swift3 可以与弹性模型一起使用,他愿意接受输入错误。
我也对缺少函数可以抛出的类型感到失望,但由于@rickster,我现在明白了,我将这样总结:假设我们可以指定函数抛出的类型,我们会有这样的东西:
enum MyError: ErrorType { case ErrorA, ErrorB }
func myFunctionThatThrows() throws MyError { ...throw .ErrorA...throw .ErrorB... }
do {
try myFunctionThatThrows()
}
case .ErrorA { ... }
case .ErrorB { ... }
问题是,即使我们不更改 myFunctionThatThrows 中的任何内容,如果我们只是向 MyError 添加一个错误案例:
enum MyError: ErrorType { case ErrorA, ErrorB, ErrorC }
我们被搞砸了,因为我们的 do/try/catch 不再详尽无遗,以及我们调用抛出 MyError
的函数的任何其他地方
像这样创建枚举:
//Error Handling in swift
enum spendingError : Error{
case minus
case limit
}
创建方法如下:
func calculateSpending(morningSpending:Double,eveningSpending:Double) throws ->Double{
if morningSpending < 0 || eveningSpending < 0{
throw spendingError.minus
}
if (morningSpending + eveningSpending) > 100{
throw spendingError.limit
}
return morningSpending + eveningSpending
}
现在检查是否有错误并处理:
do{
try calculateSpending(morningSpending: 60, eveningSpending: 50)
} catch spendingError.minus{
print("This is not possible...")
} catch spendingError.limit{
print("Limit reached...")
}
enum NumberError: Error {
case NegativeNumber(number: Int)
case ZeroNumber
case OddNumber(number: Int)
}
extension NumberError: CustomStringConvertible {
var description: String {
switch self {
case .NegativeNumber(let number):
return "Negative number \(number) is Passed."
case .OddNumber(let number):
return "Odd number \(number) is Passed."
case .ZeroNumber:
return "Zero is Passed."
}
}
}
func validateEvenNumber(_ number: Int) throws ->Int {
if number == 0 {
throw NumberError.ZeroNumber
} else if number < 0 {
throw NumberError.NegativeNumber(number: number)
} else if number % 2 == 1 {
throw NumberError.OddNumber(number: number)
}
return number
}
现在验证号码:
do {
let number = try validateEvenNumber(0)
print("Valid Even Number: \(number)")
} catch let error as NumberError {
print(error.description)
}
我试着理解 swift 中新的错误处理方式 2. 这是我所做的:我首先声明了一个错误枚举:
enum SandwichError: ErrorType {
case NotMe
case DoItYourself
}
然后我声明了一个抛出错误的方法(伙计们,这不是异常。这是一个错误。)。这是该方法:
func makeMeSandwich(names: [String: String]) throws -> String {
guard let sandwich = names["sandwich"] else {
throw SandwichError.NotMe
}
return sandwich
}
问题出在调用方。下面是调用此方法的代码:
let kitchen = ["sandwich": "ready", "breakfeast": "not ready"]
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
}
在 do
行之后,编译器说 Errors thrown from here are not handled because the enclosing catch is not exhaustive
。但在我看来它是详尽无遗的,因为 SandwichError
枚举中只有两种情况。
对于常规的 switch 语句 swift 可以理解它在处理每种情况时都是详尽无遗的。
Swift 担心您的 case 语句没有涵盖所有情况,要修复它您需要创建一个默认 case:
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
} catch Default {
print("Another Error")
}
Swift2 错误处理模型有两个要点:详尽性和弹性。它们一起归结为您的 do
/catch
语句需要捕获所有可能的错误,而不仅仅是您知道可以抛出的错误。
请注意,您没有声明函数可以抛出什么类型的错误,只声明它是否抛出。这是一个零一无穷大的问题:作为为其他人(包括你未来的自己)定义一个函数的人,你不想让你的函数的每个客户适应你的实现中的每一个变化函数,包括它可以抛出的错误。您希望调用函数的代码能够适应这种变化。
因为你的函数不能说明它抛出什么类型的错误(或者将来可能抛出),捕获它错误的 catch
块不知道它可能抛出什么类型的错误。因此,除了处理您知道的错误类型外,您还需要使用通用 catch
语句处理您不知道的错误类型——这样,如果您的函数更改了它在未来抛出的错误集,调用者仍然会捕获它的错误。
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
} catch let error {
print(error.localizedDescription)
}
但我们不止于此。多想想这个弹性的想法。按照你设计三明治的方式,你必须在你使用它们的每个地方描述错误。这意味着无论何时更改错误案例集,都必须更改使用它们的每个地方......不是很有趣。
定义你自己的错误类型背后的想法是让你集中这样的事情。您可以为错误定义一个 description
方法:
extension SandwichError: CustomStringConvertible {
var description: String {
switch self {
case NotMe: return "Not me error"
case DoItYourself: return "Try sudo"
}
}
}
然后你的错误处理代码可以要求你的错误类型来描述它自己——现在你处理错误的每个地方都可以使用相同的代码,并且也可以处理未来可能出现的错误情况。
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch let error as SandwichError {
print(error.description)
} catch {
print("i dunno")
}
这也为错误类型(或它们的扩展)支持其他报告错误的方式铺平了道路——例如,您可以在错误类型上有一个知道如何呈现 UIAlertController
用于向 iOS 用户报告错误。
我怀疑这还没有正确实施。 Swift Programming Guide 似乎绝对意味着编译器可以推断出详尽的匹配项 'like a switch statement'。它没有提到需要一个通用的 catch
才能详尽无遗。
您还会注意到错误在 try
行,而不是块的末尾,即在某些时候,编译器将能够查明哪个 try
语句在块具有未处理的异常类型。
虽然文档有点模棱两可。我浏览了“Swift 中的新功能”视频,但找不到任何线索;我会继续努力的。
更新:
我们现在已经到了 Beta 3,没有任何 ErrorType 推断的提示。我现在相信,如果这是曾经计划过的(我仍然认为它在某个时候是这样),协议扩展的动态调度可能会扼杀它。
Beta 4 更新:
Xcode 7b4 添加了对 Throws:
的文档注释支持,“应该用于记录可以抛出的错误以及原因”。我想这至少提供了 一些 机制来将错误传达给 API 消费者。有了文档,谁还需要类型系统!
另一个更新:
在花了一些时间希望自动 ErrorType
推理并弄清楚该模型的局限性之后,我改变了主意 - this 是我希望 Apple 实现的.本质上:
// allow us to do this:
func myFunction() throws -> Int
// or this:
func myFunction() throws CustomError -> Int
// but not this:
func myFunction() throws CustomErrorOne, CustomErrorTwo -> Int
又一个更新
Apple 的错误处理原理现在可用 here. There have also been some interesting discussions on the swift-evolution 邮件列表。本质上,John McCall 反对类型化错误,因为他相信大多数库最终都会包含一般性错误情况,并且类型化错误不太可能在样板代码之外增加太多代码(他使用术语 'aspirational bluff') . Chris Lattner 表示,如果 Swift3 可以与弹性模型一起使用,他愿意接受输入错误。
我也对缺少函数可以抛出的类型感到失望,但由于@rickster,我现在明白了,我将这样总结:假设我们可以指定函数抛出的类型,我们会有这样的东西:
enum MyError: ErrorType { case ErrorA, ErrorB }
func myFunctionThatThrows() throws MyError { ...throw .ErrorA...throw .ErrorB... }
do {
try myFunctionThatThrows()
}
case .ErrorA { ... }
case .ErrorB { ... }
问题是,即使我们不更改 myFunctionThatThrows 中的任何内容,如果我们只是向 MyError 添加一个错误案例:
enum MyError: ErrorType { case ErrorA, ErrorB, ErrorC }
我们被搞砸了,因为我们的 do/try/catch 不再详尽无遗,以及我们调用抛出 MyError
的函数的任何其他地方像这样创建枚举:
//Error Handling in swift
enum spendingError : Error{
case minus
case limit
}
创建方法如下:
func calculateSpending(morningSpending:Double,eveningSpending:Double) throws ->Double{
if morningSpending < 0 || eveningSpending < 0{
throw spendingError.minus
}
if (morningSpending + eveningSpending) > 100{
throw spendingError.limit
}
return morningSpending + eveningSpending
}
现在检查是否有错误并处理:
do{
try calculateSpending(morningSpending: 60, eveningSpending: 50)
} catch spendingError.minus{
print("This is not possible...")
} catch spendingError.limit{
print("Limit reached...")
}
enum NumberError: Error {
case NegativeNumber(number: Int)
case ZeroNumber
case OddNumber(number: Int)
}
extension NumberError: CustomStringConvertible {
var description: String {
switch self {
case .NegativeNumber(let number):
return "Negative number \(number) is Passed."
case .OddNumber(let number):
return "Odd number \(number) is Passed."
case .ZeroNumber:
return "Zero is Passed."
}
}
}
func validateEvenNumber(_ number: Int) throws ->Int {
if number == 0 {
throw NumberError.ZeroNumber
} else if number < 0 {
throw NumberError.NegativeNumber(number: number)
} else if number % 2 == 1 {
throw NumberError.OddNumber(number: number)
}
return number
}
现在验证号码:
do {
let number = try validateEvenNumber(0)
print("Valid Even Number: \(number)")
} catch let error as NumberError {
print(error.description)
}