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.selfT 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) 无论有没有像&lt;Type&gt; 这样的提示,你都可以构造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 &lt;T&gt; func myFunc &lt;T&gt; (s: MyStruct&lt;T&gt;) -&gt; T? let conforms = T.self is MyProtocol @Alex,您需要先构造 T 类型的实例,然后才能检查协议一致性(据我所知)如果您需要的类型 T 必须仅属于不符合 MyProtocol 的类型,您可以指定它:func myFunc&lt;T: MyProtocol&gt;(...) -&gt; 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:检查泛型类型是不是符合协议的主要内容,如果未能解决你的问题,请参考以下文章

符合两种协议的Swift泛型类型

仅适用于数值类型的泛型类型约束

Kotlin泛型总结 ★ ( 泛型类 | 泛型参数 | 泛型函数 | 多泛型参数 | 泛型类型约束 | 可变参数结合泛型 | out 协变 | in 逆变 | reified 检查泛型参数类型 )

Java 泛型泛型用法 ( 泛型类用法 | 泛型方法用法 | 泛型通配符 ? | 泛型安全检查 )

泛型类的基本使用

检查一个类是不是派生自一个泛型类