我当时在Swinject上工作,但问题困扰着我。我已经整整一天都被困在那里。我怀疑这是由于Swift是一种静态类型的语言,但我不确定。
我在这个操场上总结了我的问题
protocol Protocol {} class Class: Protocol {} let test: Protocol.Type = Class.self func printType(confromingClassType: Protocol.Type) { print(confromingClassType) } func printType<Service>(serviceType: Service.Type) { print(serviceType) } print(Class.self) // "Class" printType(serviceType: Class.self) // "Class" print(test) // "Class" printType(confromingClassType: test) // "Class" printType(serviceType: test) // "note: expected an argument list of type '(serviceType: Service.Type)'"
我尝试了不同的解决方案,例如test.self或type(of:test),但它们都不起作用。
所以我想我不能用提供为变量的通用参数来调用函数?
P.Type
P.Protocol
协议元类型有两种。对于某些协议P和符合类型C:
P
C
P.self
C.self
Any
Any.Type
Any.self
你面临的问题是,对于一个给定的通用占位符T,当T一些协议P,T.Type是 不是 P.Type -是P.Protocol。
T
T.Type
因此,如果我们回到您的示例:
protocol P {} class C : P {} func printType<T>(serviceType: T.Type) { print(serviceType) } let test: P.Type = C.self // Cannot invoke 'printType' with an argument list of type '(serviceType: P.Type)' printType(serviceType: test)
我们不能将其test作为论点printType(serviceType:)。为什么?因为test是P.Type; 并且没有替代方法T可以使serviceType:参数采用P.Type。
test
printType(serviceType:)
serviceType:
如果我们在替换P为T,参数需要P.Protocol:
printType(serviceType: P.self) // fine, P.self is of type P.Protocol, not P.Type
如果我们将 具体 类型替换为T,例如C,则参数采用C.Type:
C.Type
printType(serviceType: C.self) // C.self is of type C.Type
好的,因此我们了解到,如果可以用 具体 类型代替T,则可以将a传递C.Type给函数。我们可以代替P.Type包装的动态类型吗?不幸的是,这需要一种称为开放存在性的语言功能,该功能目前无法直接提供给用户。
但是,在访问协议类型的实例或元类型上的成员时,Swift 会 隐式打开存在对象(即,它会挖掘运行时类型并以通用占位符的形式进行访问)。我们可以在协议扩展中利用这一事实:
protocol P {} class C : P {} func printType<T>(serviceType: T.Type) { print("T.self = \(T.self)") print("serviceType = \(serviceType)") } extension P { static func callPrintType/*<Self : P>*/(/*_ self: Self.Type*/) { printType(serviceType: self) } } let test: P.Type = C.self test.callPrintType() // T.self = C // serviceType = C
这里有很多东西,所以让我们将其拆开一点:
延伸构件callPrintType()上P有一个隐含的通用占位符,Self多数民众赞成约束到P。self使用此占位符键入隐式参数。
callPrintType()
Self
self
在调用callPrintType()时P.Type,Swift会隐式地挖掘出要P.Type包装的动态类型(这是存在性的开头),并使用它来满足Self占位符。然后,它将此动态元类型传递给隐式self参数。
因此,Self将由满足C,然后可以将其转发到printType的通用占位符上T。
printType
T == P
您会注意到上述解决方法的工作原理,因为我们避免了用P通用占位符代替T。但是,为什么在协议类型时替换P的T,是T.Type 不是 P.Type?
好吧,请考虑:
func foo<T>(_: T.Type) { let t: T.Type = T.self print(t) }
如果我们代替P了T怎么办?如果T.Type是P.Type,那么我们得到的是:
func foo(_: P.Type) { // Cannot convert value of type 'P.Protocol' to specified type 'P.Type' let p: P.Type = P.self print(p) }
这是非法的;我们不能分配P.self到P.Type,因为它的类型P.Protocol,不是P.Type。
因此,结果是,如果您想要一个带有描述符合 任何 具体类型的元类型的函数参数P(而不只是一个具体的具体符合类型),那么您只需要一个P.Type参数,而不是泛型。泛型不为异构类型建模,这就是协议类型的用途。
这就是您拥有的printType(conformingClassType:):
printType(conformingClassType:)
func printType(conformingClassType: P.Type) { print(conformingClassType) } printType(conformingClassType: test) // okay
您可以传递test给它,因为它的参数类型为P.Type。但是您会注意到,这意味着我们无法传递P.self给它,因为它不是type P.Type:
// Cannot convert value of type 'P.Protocol' to expected argument type 'P.Type' printType(conformingClassType: P.self)