swift - 串行链接 do-try-catch 控制流的正确模式是什么?

标签 swift swift3 exception

我正在寻找一种模式来处理伪代码中如下所示的代码:

do:
   try planA()
catch LikelyError e:
   try planB()
catch Error e:
   print ("we're hosed, there is no planC()")

在 swift 中,将 try- 放入 catch {} block 中并不能如上所示工作,相反,它似乎需要嵌套到自己的 do {} block 中。看起来像这样:

enum ForeseenError : Error {
   case likelyProblem
   case unlikelyProblem
}

func planA () throws {
   print ("planA")
   throw ForeseenErrors.likelyProblem
}

func planB () throws {
   print ("planB")
   throw ForeseenErrors.unlikelyProblem
}

print ("Hello")
do {
   try planA()
}
catch let error as ForeseenError {
   print ("catch problem, trying planB")
   do {
      try planB()
   }
   catch let error {
      print ("Unrecoverable error from planB")

      /* if i had a planC() that potentially throws, then i'd
      have to put it here in yet another nested do-try-catch 
      control flow structure */

   }
}
catch let error {
   print ("Unrecoverable error from planA")
}

print ("EOM")

对我来说,这感觉像是一种反模式,因为它需要任意深度的嵌套来处理更多的选择。此外,虽然我可能需要不同的处理程序来处理尝试序列中不同深度的不可恢复错误,但一般来说,在最终的 catch block 中捕获所有错误会更有意义。

那么社区,针对上述逻辑我们可以做些什么呢?

最佳答案

您可以使用自定义 .catch 操作定义自己的可链接类型来解决此问题,并使用您自己的名为 attempt 的类似 try 的函数创建一个实例,如下所示以下内容:

class ChainableResult {
    let error: Error? // could also use Result<T, Error> in a generics setting
    
    init(error: Error?) {
        self.error = error
    }
}

func attempt(_ body: () throws -> ()) -> ChainableResult {
    do {
        try body()
        return ChainableResult(error: nil)
    } catch {
        return ChainableResult(error: error)
    }
}

extension ChainableResult {
    
    @discardableResult // <- dangerous
    func `catch`(_ body: (Error) throws -> ()) -> ChainableResult {
        if let originalError = self.error {
            do {
                try body(originalError)
                return ChainableResult(error: originalError)
            } catch {
                let newError = error
                return ChainableResult(error: newError)
            }
        } else {
            return ChainableResult(error: nil)
        }
    }
}

然后您可以使用这些结构,如下所示:

attempt {
    try planA()
}.catch {
    err in
    try planB()
}.catch {
    err in
    print("I give up")
}

我将 @discardableResult 注释为危险的,因为它允许您编写最终的 catch 操作可能抛出的链,在这种情况下,抛出的错误将得不到处理并被忽略。

最终,我写了自己的library for this 。它可以防止像我提到的那样的危险情况,并且也可以与异步函数体一起使用。我上面给出的示例用法片段可以逐字复制并编译。

关于swift - 串行链接 do-try-catch 控制流的正确模式是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44742703/

相关文章:

delphi - 对 EIdConnClosedGraceively 异常进行故障排除?

swift - 向 Google map 上的 KML 图层添加颜色

swift - 如何强制基于 NSObject 的基类的子类覆盖类级变量?

android - 如何在 iOS 中使用带有自定义 map 服务器的 openstreetmap,Swift 任何库?

ios - Swift - 检查一个未包装的可选变量的零

c# - 确定哪个代码行抛出异常

swift - 在 Swift 中创建的 CFSocket 不监听

Swift 3 - 以编程方式创建标题栏约束

ios - 如何使用 enumerateValues(forProperties :using:) method in MPMediaItem (Swift 3)

java - PBKDF2WithHmacSHA512 SecretKeyFactory 不可用