10

我想使用连续的 try 语句。如果一个返回错误,我想继续下一个,否则返回值。下面的代码似乎工作正常,但是我最终会得到一个大的嵌套 do catch 金字塔。在 Swift 3.0 中有更聪明/更好的方法吗?

do {
    return try firstThing()
} catch {
    do {
        return try secondThing()
    } catch {
        return try thirdThing()
    }
}
4

3 回答 3

17

如果不需要从这些函数调用中抛出的实际错误,则可以使用try?将结果转换为可选项,并使用 nil-coalescing 运算符链接调用??

例如:

if let result = (try? firstThing()) ?? (try? secondThing()) ?? (try? thirdThing()) {
    return result
} else {
    // everything failed ...
}

或者,如果在一切都失败的情况下应该抛出最后一个方法的错误,请使用try?除最后一个方法调用之外的所有方法:

return (try? firstThing()) ?? (try? secondThing()) ?? (try thirdThing())
于 2017-08-14T11:01:53.227 回答
14

如果 Martin 的回答对您的口味来说太简洁,您可以使用单独的 catch 块。

do {
    return try firstThing()
} catch {}

do {
    return try secondThing()
} catch {}

do {
    return try thirdThing()
} catch {}

return defaultThing()

由于每个抛出函数的结果都会立即返回,因此不需要嵌套。

于 2017-08-14T11:09:09.660 回答
4

另一种方法是编写一个将所有抛出函数作为参数的函数。它返回第一个成功执行的或 nil。

func first<T>(_ values: (() throws -> T)...) -> T? {
    return values.lazy.flatMap({ (throwingFunc) -> T? in
        return try? throwingFunc()
    }).first
}

惰性确保仅在找到第一个匹配项之前调用这些值。这样做,您还可以非常快速地添加很多案例。

您可以像这样使用该功能

return first(firstThing, secondThing, thirdThing) ?? "Default"

我还包括了我用来在操场上测试它的代码:

enum ThingError: Error {
    case zero
}

func firstThing() throws -> String {
    print("0")
    throw ThingError.zero
    return "0"
}

func secondThing() throws -> String {
    print("1")
    return "1"
}

func thirdThing() throws -> String {
    print("2")
    return "B"
}

func first<T>(_ values: (() throws -> T)...) -> T? {
    return values.lazy.flatMap({ (throwingFunc) -> T? in
        return try? throwingFunc()
    }).first
}

func tryThings() -> String {
    return first(firstThing, secondThing, thirdThing) ?? "Default"
}

tryThings() // prints "0" and "1"
于 2017-08-14T12:35:16.207 回答