在测试是否存在属性时,正确使用防护是什么

时间:2017-09-26 12:24:26

标签: swift

在我的代码中,我正在传递字典。该字典可以包含错误字符串。喜欢这个

func handleSomething(_ json: [String: Any]) -> (Bool, String?) {
    if let error = json["error"] as? String {
        print("I have an error: \(error)")
        return (false, error)
    }
    ...
    return (true, nil)
}

我认为这是一个使用后卫的好地方,但代码并不简洁

func handleSomething(_ json: [String: Any]) -> Bool {
    let error = json["error"] as? String
    guard error == nil else {
        print("I have an error: \(error!)")
        return (false, error)
    }
    ...
    return (true, nil)
}

有更好的方法可以做到这一点,所以分配和检查可以在一行完成吗?如果不是这是正确使用警卫?

更新:添加了返回值,以更好地说明问题的真实意图。

1 个答案:

答案 0 :(得分:2)

一般来说,你应该强烈避免绕过[String: Any]。您应该先将其解析为更强大的数据类型,并且应该在那里将错误情况分开,通常使用枚举:

struct ServerData {
    // good data; no errors
}

enum ServerResponse {
    case success(ServerData)
    case error(String) // Return this if error is set
}

有了这个,我们的函数大多只需ServerData,所以错误已经处理完毕。在可能的情况下,不要为问题提出巧妙的解决方法,而是要解决整个问题。

那就是说,仍然有解析器本身可以处理,这种情况可以在那里出现,即使你有更强的类型。在许多情况下,你经常使用这种防护/日志模式,并且它绝对值得避免。拔出一个函数:

func noErrorOrLog(_ json: [String: Any]) -> Bool {
    if let error = json["error"] as? String {
        print("I have an error: \(error)")
        return false
    }

    return true
}

现在实际处理函数可以使用guard并避免重复日志记录:

func handleSomething(_ json: [String: Any]) -> Bool {
    guard noErrorOrLog(json) else {
        return false
    }
    // ...
}

在某些情况下,这没有任何意义(如果你或许正好这样做一次),在这种情况下if-let是正确的工具。创建令人困惑的代码以创建guard会适得其反。但在许多情况下,您可以完全避免此问题(通过较早的类型过滤错误),或通过移动到函数来避免重复。