我最初发布此问题是对此问题here的后续编辑,但它似乎足够独立,可以作为自己的问题。所以这就是。
我正在尝试验证[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文档,但我仍然有点困惑。有没有办法做这种事情?
答案 0 :(得分:4)
我害怕这种方法不起作用。原因是,您正在尝试将动态运行时行为与静态编译时检查混合在一起。
功能:
func objectIsType<T>(object: Any, someObjectOfType: T.Type) -> Bool {
return object is T
}
使用一种黑客/技巧来做一些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)
显然,伪装虚拟值有点垃圾,因此元类型解决方案感觉更好。但不幸的是,它给出了一个错觉,即存在一个可用于引用运行时类型的变量,然后可以与is
或as
一起使用。但这就是幻觉。你不能用Swift的基本类型做到这一点。他们只是不支持它。
请记住,当您在编译时调用泛型函数时会发生什么 ,Swift会为您编写一个与您正在使用它的类型所需的函数版本。因此,当您提供Int.self
时,实际发生的是编译器为您写入此函数:
objectIsType(obj: Any, someObjectOfType: Int.Type) -> Bool {
return obj is Int
}
我不能强调这一点,它在编译时完成。因此,无法使用在运行时确定的各种不同类型调用此函数并使其工作。相反,当您声明字典时,您声明的字典中的值为Any.Type
。由于Any.Type
是所有其他类型的超类型,因此您仍然可以为其分配String.Type
,但实际上您将要存储的是Any.Type
。 protocol<>.Type
实际上是另一种说Any.Type
的方式,因为Any
就是这样:它被定义为typealias Any = protocol<>
。即使你确实得到了你的代码进行编译,它总会返回true,因为你问的是objectIsType(Any, Any.Type)
(我不太确定为什么它不会,除了你需要解开你从字典中取出的值,因为它是可选的,但是应该这样做:if let obj = validatee[key] where !objectIsType(obj, type.self) { selfDestruct() }
)
基本上,如果你想要这种运行时动态类型的行为,你可能不得不坚持@objc
土地,除非其他人知道一个好的技巧。
然而,这就是我要问的问题:你究竟想要实现什么目标?你真的需要这种动态行为吗?很多时候,您可以使用编译时泛型来改写实际目标,而不是转换Any
。不总是,但经常。