不幸的是,您目前只能使用带有 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)
}
}
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
这种方法的唯一限制是,鉴于我们使用T.self 执行is 检查,我们必须强制执行T.self == base。例如,我们不能接受AnyType(D.self as C.Type),因为T.self 将是C.self 而base 将是D.self。
不过,在您的情况下,这应该不是问题,因为我们只是从编译时已知的元类型构造 AnyType。
但是,如果您无法控制元类型的创建(即您从 API 获得它们),那么您可以使用它们做的事情就会受到相当多的限制。
作为@adev says,您可以使用type(of:) 来获取给定实例的动态元类型,并使用== 运算符来确定两个元类型是否等效。然而,这种方法的一个问题是它忽略了类层次结构和协议,因为子类型元类型不会与超类型元类型进行比较。
类情况下的一个解决方案是使用Mirror,如in this Q&A所示:
/// 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
我们使用sequence(first:next:) 从x 的动态类型到它可能具有的任何超类元类型创建一系列元类型。
但是,此方法仍然不适用于协议。希望该语言的未来版本将提供更丰富的反射 API,允许您比较两个元类型值之间的关系。
但是,鉴于上述能够使用Mirror 的知识,我们可以通过单独处理类元类型来使用它从AnyType 包装器中解除上述T.self == base 的限制:
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
T.self 是类元类型的情况应该是T.self != base 的唯一情况,与协议一样,当T 是某个协议时P,T.Type 是P.Protocol,这是类型协议本身。而且目前这个类型只能保存P.self这个值。