我需要为不同的课做不同的事情。例如,我已经创建了协议
protocol ResponseProtocol {
associatedtype ResponseType: Any
}
protocol MappableProtocol {
init(map: String)
}
我正在添加我的数据类MyDto
class MyDto: MappableProtocol {
required init(map: String) { }
}
和3个不同的响应类
class A1: ResponseProtocol {
typealias ResponseType = String
}
class A2: ResponseProtocol {
typealias ResponseType = MyDto
}
class A3: ResponseProtocol {
typealias ResponseType = [MyDto]
}
现在我需要根据ResponseType
做不同的事情。
我尝试了这段代码,但我遇到了Array
s
class API {
func test<T: ResponseProtocol>(a: T) -> String {
if T.ResponseType.self is String.Type {
return "String"
}
if T.ResponseType.self is MappableProtocol.Type {
return "MappableProtocol"
}
if T.ResponseType.self is [Any].Type {
return "Array<Any>" // Why is this false?
}
if T.ResponseType.self is [MappableProtocol].Type {
return "Array<MappableProtocol>" //Why is this false?
}
if T.ResponseType.self is [MyDto].Type {
return "Array<MyDto>" // Why is only this true?
}
return "notFound"
}
}
let api = API()
let t1 = api.test(a: A1())
let t2 = api.test(a: A2())
let t3 = api.test(a: A3())
在我的游乐场控制台中,对于数组A3
,我看到Array<MyDto>
,但我期望第一次返回数组Array<Any>
。
如何检查Array
属于Element
的{{1}}?
答案 0 :(得分:2)
问题是虽然[MyDto]
的实例可以自由转换为[MappableProtocol]
和[Any]
,但这些实际上只是编译器所做的神奇转换场景(更多信息见this Q&A)。
同样的转换对于元类型值并不存在,这就是为什么Swift说[MyDto].Type
不是[MappableProtocol].Type
也不是[Any].Type
- 它们是不相关的元类型类型。
在您的情况下,最简单的解决方案可能就是忘记使用元类型,而只是声明test(a:)
的不同重载来处理不同的ResponseType
类型。
// 'default' overload of test(a:)
func test<T : ResponseProtocol>(a: T) -> String {
return "notFound"
}
func test<T : ResponseProtocol>(a: T) -> String where T.ResponseType == String {
return "String"
}
func test<T : ResponseProtocol>(a: T) -> String where T.ResponseType : MappableProtocol {
return "MappableProtocol"
}
func test<T : ResponseProtocol>(a: T) -> String where T.ResponseType == [MyDto] {
return "Array<MDto>"
}
// overload of test(a:) that accepts a type that conforms to ResponseProtocol, where the
// ResponseType is an Array with arbitrary Element type.
func test<T : ResponseProtocol, ResponseTypeElement : MappableProtocol>(a: T) -> String
where T.ResponseType == [ResponseTypeElement]
{
return "Array<MappableProtocol>"
}
print(test(a: A1())) // String
print(test(a: A2())) // MappableProtocol
print(test(a: A3())) // Array<MyDto>
// A MappableProtocol with a ResponseType that conforms to MappableProtocol,
// but isn't MyDto.
class Foo : MappableProtocol { required init(map: String) { } }
class A4 : ResponseProtocol { typealias ResponseType = [Foo] }
print(test(a: A4())) // Array<MappableProtocol>
(为了简化事情,我删除了您的API
课程)
编译器将简单地解决在编译时调用哪个重载,而不是让运行时跳过大量的类型转换箍。
如果坚持使用元类型值,一种可能的解决方案是为Array
定义虚拟协议以符合(参见例如{{ 3}}),然后我们可以将元类型值转换为。然后,我们可以声明elementType
静态要求,以便提取Array
的{{1}}元类型值,然后我们可以检查其类型以确定数组是什么可转换为。
例如,如果我们定义Element.self
并将Array
定义为_ArrayProtocol
:
protocol _ArrayProtocol {
static var elementType: Any.Type { get }
}
extension Array : _ArrayProtocol {
static var elementType: Any.Type {
return Element.self
}
}
我们现在可以像这样使用test(a:)
:
func test<T : ResponseProtocol>(a: T) -> String {
if T.ResponseType.self is String.Type {
return "String"
}
if T.ResponseType.self is MappableProtocol.Type {
return "MappableProtocol"
}
// attempt to cast the T.ResponseType.self metatype value to the existential metatype
// type _ArrayProtocol.Type (i.e a type that conforms to _ArrayProtocol),
// in this case, that's only ever Array.
if let responseType = T.ResponseType.self as? _ArrayProtocol.Type {
// switch on the element type, attempting to cast to different metatype types.
switch responseType.elementType {
case is MyDto.Type:
return "Array<MyDto>"
case is MappableProtocol.Type:
return "Array<MappableProtocol>"
default:
return "Array<Any>"
}
}
return "notFound"
}
print(test(a: A1())) // String
print(test(a: A2())) // MappableProtocol
print(test(a: A3())) // Array<MyDto>
print(test(a: A4())) // Array<MappableProtocol>