Swift 中的类型检查和验证

Type checking and validation in Swift

我最初发布这个问题是作为这个问题 的后续编辑,但它似乎足够独立,可以作为一个独立的问题。所以就在这里。

我正在尝试验证 [String: Any] 字典的值是否属于我期望的类型。我想做的是这样的:

func objectIsType<T>(object: Any, someObjectOfType: T.Type) -> Bool {
    return object is T
}


let validator: [String: Any.Type] = [
    "gimme an int": Int.self,
    "this better be a string": String.self
]

let validatee: [String: Any] = [
    "gimme an int": 3,
    "this better be a string": "it is!"
]

for (key, type) in validator {
    if !objectIsType(validatee[key], type) {
        selfDestruct()
    }
}

但是我得到了错误,<>protocol.Type is not convertible to T.type. 如果我不使用 objectIsType 函数,而是直接使用 is,使循环

for (key, type) in validator {
    if !(validatee[key] is type) {
        selfDestruct()
    }
}

我收到错误,'type' is not a type。我看过 Swift Metatype 文档,但我还是有点困惑。有做这种事情的好方法吗?

恐怕这种方法行不通。原因是,您试图将动态 运行 时行为与静态编译时检查混合。

函数:

func objectIsType<T>(object: Any, someObjectOfType: T.Type) -> Bool {
    return object is T
}

正在使用一种 hack/trick 来做一些 Swift 不喜欢做的事情,即在没有上下文的情况下指定通用占位符的类型。那是什么意思?嗯,真的,你想做的是这样的:

func objectIsType<T>(obj: Any) -> Book { return obj is T }

然后这样称呼它:

// See if myAny is an Integer...
objectIsType<Int>(myAny)

但是在 Swift 中(不像在 C++ 中),你不能这样做。你不能告诉编译器“使用 Int 作为占位符 T”。 Swift 只会让您从调用它的上下文中确定 T。因此,您使用的技巧是通过其他方式修复 T。在这里,您使用的是类型的元类型。另一种方法是提供一个未使用的虚拟值(如您的原始问题)。

func objectIsType<T>(object: Any, dummyValOfThatType: T) -> Bool {
    // no use of the dummy value is made...
    return object is T
}

// then, to detect Ints, supply a dummy Int
objectIsType(myAny, 1)

显然,虚拟值的魔法有点垃圾,因此元类型解决方案感觉更好。但不幸的是,它给人一种错觉,即那里有一个变量可以用来引用 运行 时间类型,然后可以与 isas 一起使用。但这只是一个幻觉。你不能用 Swift 的基本类型来做到这一点。他们就是不支持。

请记住,当您调用泛型函数时,在编译时、Swift 会为您编写该类型所需的函数版本你正在使用它。所以当你提供一个 Int.self 时,实际发生的是编译器给你写了这个函数:

objectIsType(obj: Any, someObjectOfType: Int.Type) -> Bool {
    return obj is Int
}

我怎么强调都不为过,它在编译时这样做了。因此,无法使用在 运行 时间确定的各种不同类型来调用此函数并让它工作。相反,当您声明字典时,您是在声明一个充满 Any.Type 值的字典。由于 Any.Type 是所有其他类型的超类型,您仍然可以将 String.Type 分配给它,但实际上您要存储的只是 Any.Typeprotocol<>.Type 实际上是 Any.Type 的另一种说法,因为 Any 就是这样:它被定义为 typealias Any = protocol<>。即使您确实编译了代码,它也总是 return true,因为您要问的是 objectIsType(Any, Any.Type)

(我不太清楚为什么它不会,除了你需要解包你从字典中得到的值,因为它是可选的,但应该这样做:if let obj = validatee[key] where !objectIsType(obj, type.self) { selfDestruct() } )

基本上,如果您想要这种 运行 时间动态类型的行为,您可能不得不坚持使用 @objc 土地,除非那里的其他人知道一个好技巧。

然而,这就是我要问的问题:你到底想达到什么目的?你真的需要这种动态行为吗?很多时候,您可以根据编译时泛型重新表述您的实际目标,而不是胡乱转换 Any。不总是,但经常。