Swift:检查泛型类型是不是符合协议
Posted
技术标签:
【中文标题】Swift:检查泛型类型是不是符合协议【英文标题】:Swift: check if generic type conforms to protocolSwift:检查泛型类型是否符合协议 【发布时间】:2015-01-24 10:51:14 【问题描述】:我有一个这样定义的协议:
protocol MyProtocol
...
我也有一个通用结构:
struct MyStruct <T>
...
我终于有了一个通用函数:
func myFunc <T> (s: MyStruct<T>) -> T?
...
如果类型 T 符合 MyProtocol,我想在函数内部进行测试。本质上我希望能够做到(~伪代码):
let conforms = T.self is MyProtocol
但这会引发编译器错误:
error: cannot downcast from 'T.Type' to non-@objc protocol type 'MyProtocol'
let conforms = T.self is MyProtocol
~~~~~~ ^ ~~~~~~~~~~
我也尝试过变体,例如T.self is MyProtocol.self
、T is MyProtocol
,并使用==
代替is
。到目前为止,我还没有到任何地方。有什么想法吗?
【问题讨论】:
【参考方案1】:我不得不说@Alex 想检查T
类型是否符合协议而不是s
。有的答主没看清楚。
检查T
类型是否符合这样的协议:
if let _ = T.self as? MyProtocol.Type
// T conform MyProtocol
或
if T.self is MyProtocol.Type
// T conform MyProtocol
【讨论】:
如果 MyProtocol 是 CaseIterable(具有关联类型的协议)怎么办?它显示错误:“CaseIterable”只能用作通用约束,因为它具有 Self 或关联的类型要求。 @zgjie 你的评论找到答案了吗?【参考方案2】:有点晚了,但您可以使用as ?
测试测试是否有响应协议:
if let currentVC = myViewController as? MyCustomProtocol
// currentVC responds to the MyCustomProtocol protocol =]
编辑:短一点:
if let _ = self as? MyProtocol
// match
并使用警卫:
guard let _ = self as? MyProtocol else
// doesn't match
return
【讨论】:
这需要T
的实例,但问题是关于通用类型。因此卡洛斯的答案更好:***.com/a/52787263/1311272
Carlos Chaguendo 的答案应该是 IMO 接受的答案。
这不能回答问题
没错,它没有按要求回答问题,但回答了 my 问题【参考方案3】:
最简单的答案是:不要那样做。改用重载和约束,并在编译时预先确定所有内容,而不是在运行时动态测试内容。运行时类型检查和编译时泛型就像牛排和冰淇淋一样——两者都很好,但混合起来有点奇怪。
考虑这样的事情:
protocol MyProtocol
struct MyStruct <T> let val: T
func myFunc<T: MyProtocol>(s: MyStruct<T>) -> T?
return s.val
func myFunc<T>(s: MyStruct<T>) -> T?
return nil
struct S1: MyProtocol
struct S2
let m1 = MyStruct(val: S1())
let m2 = MyStruct(val: S2())
myFunc(m1) // returns an instance of S1
myFunc(m2) // returns nil, because S2 doesn't implement MyProtocol
缺点是,如果 T 在运行时支持协议,则无法动态建立:
let o: Any = S1()
let m3 = MyStruct(val: o)
myFunc(m3) // will return nil even though o
// does actually implement MyProtocol
但是,老实说,您真的需要在泛型函数中这样做吗?如果您不确定某事物的实际类型是什么,更好的选择可能是预先弄清楚,而不是推迟到以后再在泛型函数中推动它以找出答案。
【讨论】:
+1,很好的答案。特别喜欢“运行时类型检查和编译时泛型就像牛排和冰淇淋一样——两者都很好,但混合起来有点奇怪。”? 是的……除了重载和约束无法完成工作的边缘情况。考虑一个实现协议JSONEncodable
的扩展方法,它需要init(json: JSON) throws
。我们希望Array
实现JSONEncodable
,但前提是它的元素也是JSONEncodable
。我们不能将继承子句与约束结合起来,所以我们必须在init
的实现中使用某种类型检查,如果Element
不是JSONEncodable
可能会抛出错误。可悲的是,这似乎是不可能的 AFAICT。
我应该补充一点,上面的难题可以通过使用中间类型作为 thunk 来解决,但这是一个非常不雅的解决方案。
@GregoryHigley 现在应该可以在 Swift 4.1 中使用条件一致性 (swift.org/blog/conditional-conformance)
无论有没有像<Type>
这样的提示,你都可以构造MyStruct
,这很酷,它可以告诉你要做什么。对于其他尝试代码的人,Swift 4 需要在第一个构造函数参数上使用 _
【参考方案4】:
让符合 = T.self 是 MyProtocol.Type
【讨论】:
这是最好的答案,因为它回答了原始问题“如何做:let conforms = T.self is MyProtocol
”。
同意,这个问题的最佳答案!【参考方案5】:
如果您想处理T
类型的多个案例,您还可以利用swift 的switch case pattern matching:
func myFunc<T>(s: MyStruct<T>) -> T?
switch s
case let sType as MyProtocol:
// do MyProtocol specific stuff here, using sType
default:
//this does not conform to MyProtocol
...
【讨论】:
这也没有回答问题,因为它在一个实例中测试符合协议而不是直接的类型。【参考方案6】:您需要将协议声明为@objc
:
@objc protocol MyProtocol
...
来自 Apple 的“The Swift Programming Language”一书:
只有当您的协议标有@objc 属性时,您才能检查协议一致性,如上面的 HasArea 协议所示。这个属性表明协议应该暴露给 Objective-C 代码,在 Using Swift with Cocoa and Objective-C 中有描述。即使您不与 Objective-C 互操作,如果您希望能够检查协议一致性,您也需要使用 @objc 属性标记您的协议。
还要注意@objc 协议只能被类采用,不能被结构或枚举采用。如果您将协议标记为 @objc 以检查一致性,您将能够将该协议仅应用于类类型。
【讨论】:
即使这样,我仍然得到同样的错误。@objc protocol MyProtocol struct MyStruct <T> func myFunc <T> (s: MyStruct<T>) -> T? let conforms = T.self is MyProtocol
@Alex,您需要先构造 T 类型的实例,然后才能检查协议一致性(据我所知)如果您需要的类型 T 必须仅属于不符合 MyProtocol 的类型,您可以指定它:func myFunc<T: MyProtocol>(...) -> T?
【参考方案7】:
对于测试用例,我会像这样检查一致性:
let conforms: Bool = (Controller.self as Any) is Protocol.Type
【讨论】:
【参考方案8】:现代答案将是这样的:(Swift 5.1)
func myFunc < T: MyProtocol> (s: MyStruct<T>) -> T? ...
【讨论】:
??这如何回答这个问题?以上是关于Swift:检查泛型类型是不是符合协议的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin泛型总结 ★ ( 泛型类 | 泛型参数 | 泛型函数 | 多泛型参数 | 泛型类型约束 | 可变参数结合泛型 | out 协变 | in 逆变 | reified 检查泛型参数类型 )