fra*_*yan 7 types swift metatype
我需要保留Swift元类型的集合并编写一个函数,该函数将检查给定对象是否是其中一个的实例.我可以在Java中轻松完成:
Class c = x.getClass();
c.isInstance(someObj)
Run Code Online (Sandbox Code Playgroud)
但是,我不知道如何在Swift中这样做:
var isInt = 7 is Int.Type // compiles
let x = Int.self
var isInt = 7 is x // compiler error - Use of undeclared type 'x'
Run Code Online (Sandbox Code Playgroud)
这甚至可以在Swift中完成吗?
不幸的是,你现在只能使用带有is运算符的命名类型,你还不能使用任意的元类型值(尽管你应该能够真正使用IMO ).
假设您可以控制要比较的元类型的创建,一个实现相同结果的解决方案是创建一个包装器类型,其初始化程序存储一个is对通用占位符执行检查的闭包:
struct AnyType {
let base: Any.Type
private let _canCast: (Any) -> Bool
/// Creates a new AnyType wrapper from a given metatype.
/// The passed metatype's value **must** match its static value,
/// i.e `T.self == base`.
init<T>(_ base: T.Type) {
precondition(T.self == base, """
The static value \(T.self) and dynamic value \(base) of the passed \
metatype do not match
""")
self.base = T.self
self._canCast = { $0 is T }
}
func canCast<T>(_ x: T) -> Bool {
return _canCast(x)
}
}
Run Code Online (Sandbox Code Playgroud)
protocol P {}
class C : P {}
class D : C {}
let types = [
AnyType(P.self), AnyType(C.self), AnyType(D.self), AnyType(String.self)
]
for type in types {
print("C instance can be typed as \(type.base): \(type.canCast(C()))")
print("D instance can be typed as \(type.base): \(type.canCast(D()))")
}
// C instance can be typed as P: true
// D instance can be typed as P: true
// C instance can be typed as C: true
// D instance can be typed as C: true
// C instance can be typed as D: false
// D instance can be typed as D: true
// C instance can be typed as String: false
// D instance can be typed as String: false
Run Code Online (Sandbox Code Playgroud)
这种方法的唯一限制是,鉴于我们正在执行is检查T.self,我们必须强制执行T.self == base.例如,我们不能接受AnyType(D.self as C.Type),因为那么T.self将C.self同时base会D.self.
但是,在您的情况下,这不应该是一个问题,因为我们只是AnyType从编译时已知的元类型构造.
但是,如果您无法控制元类型的创建(即您从API中获取它们),那么您可以使用它们进行更多限制.
正如@adev所说,您可以使用type(of:)以获取给定实例的动态元类型,并使用==运算符来确定两个元类型是否相等.但是,这种方法的一个问题是它忽略了类层次结构和协议,因为子类型元类型不会与超类型元类型进行比较.
/// Returns `true` iff the given value can be typed as the given
/// **concrete** metatype value, `false` otherwise.
func canCast(_ x: Any, toConcreteType destType: Any.Type) -> Bool {
return sequence(
first: Mirror(reflecting: x), next: { $0.superclassMirror }
)
.contains { $0.subjectType == destType }
}
class C {}
class D : C {}
print(canCast(D(), toConcreteType: C.self)) // true
print(canCast(C(), toConcreteType: C.self)) // true
print(canCast(C(), toConcreteType: D.self)) // false
print(canCast(7, toConcreteType: Int.self)) // true
print(canCast(7, toConcreteType: String.self)) // false
Run Code Online (Sandbox Code Playgroud)
我们正在使用sequence(first:next:)动态类型x通过它可能具有的任何超类元类型来创建元类型序列.
但是,此方法仍无法使用协议.希望该语言的未来版本将提供更丰富的反射API,允许您比较两个元类型值之间的关系.
但是,鉴于上述能够使用的知识Mirror,我们可以通过单独处理类元类型来使用它来解除上述T.self == base对AnyType包装器的限制:
struct AnyType {
let base: Any.Type
private let _canCast: (Any) -> Bool
/// Creates a new AnyType wrapper from a given metatype.
init<T>(_ base: T.Type) {
self.base = base
// handle class metatypes separately in order to allow T.self != base.
if base is AnyClass {
self._canCast = { x in
sequence(
first: Mirror(reflecting: x), next: { $0.superclassMirror }
)
.contains { $0.subjectType == base }
}
} else {
// sanity check – this should never be triggered,
// as we handle the case where base is a class metatype.
precondition(T.self == base, """
The static value \(T.self) and dynamic value \(base) of the passed \
metatype do not match
""")
self._canCast = { $0 is T }
}
}
func canCast<T>(_ x: T) -> Bool {
return _canCast(x)
}
}
print(AnyType(D.self as C.Type).canCast(D())) // true
Run Code Online (Sandbox Code Playgroud)
其中的情况下T.self是一个类元类型应该是其中唯一的情况下T.self != base,与协议,当T是一些协议P,T.Type是P.Protocol,它是协议本身的类型.目前,这种类型只能保持价值P.self.
| 归档时间: |
|
| 查看次数: |
1381 次 |
| 最近记录: |