【问题标题】:Possible to make a generic type checking function in Swift?可以在 Swift 中创建泛型类型检查功能吗?
【发布时间】:2016-11-13 18:09:15
【问题描述】:

我正在向我继承的 Web API 框架添加更好的错误处理。目前它做了一堆强制转换,当数据与预期不匹配时会导致崩溃。

我想将 as! 的所有用法替换为检查类型并在失败时抛出异常(带有详细说明)的函数。

这就是我到目前为止所做的:

func checkType<T>(type: AnyClass, value: T?, name: String) throws -> T {
    guard let value = value else {
        let message = "[\(name)] Expected \(type), but value was nil"
        throw PlaidsterError.InvalidType(message)
    }

    guard value.dynamicType.self == type else {
        let message = "[\(name)] Expected \(type), but it was an \(value.dynamicType.self) with value: \(value)"
        throw PlaidsterError.InvalidType(message)
    }

    return value
}

但这有多个问题。它只能接受对象类型,如果类不完全匹配则失败(例如 NSString 失败,因为它实际上是一个 __NSCFString),并且它不能用于任何类型,即 String、Int、Double...

我似乎无法为Any 值类型找到AnyClass 的同义词,这是第一个问题。此外,似乎不可能做类似value.dynamicType.self is type 的事情,因为它说type 不是一种类型。


有可能做我想做的事吗?有没有更好的方法来进行这种类型检查,而不需要大量的样板代码分散在解析代码中?

我的目标是得到这样甚至更简单的东西:

public struct PlaidCategory {

    // MARK: Properties
    public let id: String
    public let hierarchy: [String]
    public let type: String

    // MARK: Initialization
    public init(category: [String: Any]) throws {
        id = try checkType(String.self, value: category["id"], name: "id")
        hierarchy = try checkType([String].self, value: category["hierarchy"], name: "hierarchy")
        type = try checkType(String.self, value: category["type"], name: "type")
}

}

【问题讨论】:

  • 我建议你看看 github 上众多的 swift json 解析器之一,看看你是否能找到一些有趣的“github swift json”。 SwiftyJSON、Gloss 等。

标签: swift generics introspection


【解决方案1】:

您可以使用另一个泛型类型参数编写类似的内容。

func checkType<T, U>(type: U.Type, value: T?, name: String) throws -> U {
    guard let value = value else {
        let message = "[\(name)] Expected \(type), but value was nil"
        throw PlaidsterError.InvalidType(message)
    }

    guard let result = value as? U else {
        let message = "[\(name)] Expected \(type), but it was an \(value.dynamicType) with value: \(value)"
        throw PlaidsterError.InvalidType(message)
    }

    return result
}

并将其用作:

do {
    let n: Any = 3
    let t = try checkType(Int.self, value: n, name: "n")
    print(t) //->3
} catch let error {
    print(error)
}
do {
    let n: Any = "x"
    let t = try checkType(Int.self, value: n, name: "n")
    print(t)
} catch let error {
    print(error) //->InvalidType("[n] Expected Int, but it was an _NSContiguousString with value: x")
}

我希望这适用于您的 PlaidCategory.init(category:),但尚未经过测试。


还有一个。如果仅在返回类型可推断的情况下使用,则无需传递type 参数。

func checkType<T, U>(value: T?, name: String) throws -> U {
    guard let value = value else {
        let message = "[\(name)] Expected \(U.self), but value was nil"
        throw PlaidsterError.InvalidType(message)
    }

    guard let result = value as? U else {
        let message = "[\(name)] Expected \(U.self), but it was an \(value.dynamicType) with value: \(value)"
        throw PlaidsterError.InvalidType(message)
    }

    return result
}

并将其用作:

do {
    let n: Any = "x"
    let t: Int = try checkType(n, name: "n")
    print(t)
} catch let error {
    print(error) //->InvalidType("[n] Expected Int, but it was an String with value: x")
}

或:

public struct PlaidCategory {

    // MARK: Properties
    public let id: String
    public let hierarchy: [String]
    public let type: String

    // MARK: Initialization
    public init(category: [String: Any]) throws {
        id = try checkType(category["id"], name: "id")
        hierarchy = try checkType(category["hierarchy"], name: "hierarchy")
        type = try checkType(category["type"], name: "type")
    }
}

【讨论】:

  • 谢谢这是完美的!不敢相信我也没有想到该类型的泛型!您的第二个答案效果很好(在 Playground 中测试)并且更加简洁。
  • 另外,由于我正在解析 JSON,所以我专门使用字典来获取值,所以我添加了第二个便利函数来消除输入两次名称的需要:func checkType&lt;U&gt;(dictionary: Dictionary&lt;String, Any&gt;, name: String) throws -&gt; U { let value = dictionary[name]; return try checkType(value, name: name) }
猜你喜欢
  • 1970-01-01
  • 2020-09-18
  • 2010-10-13
  • 2021-08-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多