我当时在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.Protocol
描述协议本身的类型(它可以保存的唯一值是P.self
)。P.Type
描述了符合协议的具体类型。它可以容纳的值C.self
,但 不会 P.self
因为协议不符合自己(虽然一个例外是Any
,因为Any
是顶级型,因此任何元类型的值可以分型为Any.Type
,其中包括Any.self
)。你面临的问题是,对于一个给定的通用占位符T
,当T
一些协议P
,T.Type
是 不是 P.Type
-是P.Protocol
。
因此,如果我们回到您的示例:
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
。
如果我们在替换P
为T
,参数需要P.Protocol
:
printType(serviceType: P.self) // fine, P.self is of type P.Protocol, not P.Type
如果我们将 具体 类型替换为T
,例如C
,则参数采用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()
时P.Type
,Swift会隐式地挖掘出要P.Type
包装的动态类型(这是存在性的开头),并使用它来满足Self
占位符。然后,它将此动态元类型传递给隐式self
参数。
因此,Self
将由满足C
,然后可以将其转发到printType
的通用占位符上T
。
T.Type
不P.Type
何时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:)
:
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)
问题内容: 为什么此代码无法编译? 为什么我不能将类变量传递给? 问题答案: 该操作符对引用类型,像,而不是对象,如。您可能想要类似 旁注:如果编写,您的代码将更加简洁 但是,我不确定是否需要某种方法。
问题内容: 我定义了一个Java函数: 一种调用方式是这样的: 为什么不能通过显式传递泛型类型参数来调用它?: 我从编译器得到错误。 问题答案: 当Java编译器无法自行推断静态方法的参数类型时,您始终可以使用完整的合格方法名称Class来传递它。<类型> method();
编译(使用clang 6 . 0 . 0 for x86 _ 64 on Linux so System V ABI,flags:< code >-O3-March = broad well )到 如果我没看错的话,这就是正在发生的事情: 传递一个指向的堆栈()的指针以用于其返回值,因此在返回之后通过包含的结果。 然后将此结果立即复制到较低的堆栈地址到。 然后被调用,它将从中读取其参数。 为什么下
问题内容: 我正在使用py.test来测试包装在python类MyTester中的某些DLL代码。为了进行验证,我需要在测试期间记录一些测试数据,然后再进行更多处理。由于我有许多test _…文件,因此我想在大多数测试中重用测试器对象的创建(MyTester的实例)。 由于tester对象是获得DLL变量和函数的引用的对象,因此我需要将DLL变量的列表传递给每个测试文件的tester对象(要记录的
问题内容: 根据System V X86-64ABI,应用程序中的函数调用使用以下寄存器序列传递整数参数: 但是系统调用参数(而不是系统调用号)在另一个寄存器序列中传递: 为什么内核使用而不是第四个参数?它与当下没有保留的事实有某种联系吗? 问题答案: X86-64系统调用使用说明。该指令将返回地址保存到,然后从MSR 加载。即立即被摧毁。这就是为什么必须替换系统调用ABI的原因。 相同的指令也保
问题内容: 在猫鼬文档中,它经常列出某些查询运算符(如)的可选回调,但是,它没有提及回调采用的参数(参数)。他们是什么,我怎么知道? 另外,如果,等都是可选的,我想在结束时指定一个回调,我必须在传递值,或空物体或我可以只指定回调- 和软管做猫鼬知道吗? 问题答案: 对于几乎所有的猫鼬查询,所提供的函数将在文档中所述的节点回调模式 中用两个参数调用: 在Mongoose中将回调传递给查询的任何地方,