【问题标题】: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。到目前为止,我还没有到任何地方。有什么想法吗?

【问题讨论】:

    标签: generics swift


    【解决方案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 的实例,但问题是关于通用类型。因此卡洛斯的答案更好:stackoverflow.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? {    ... }
      

      【讨论】:

      • ??这如何回答这个问题?
      猜你喜欢
      • 2014-12-09
      • 1970-01-01
      • 2018-04-24
      • 2021-10-13
      • 1970-01-01
      • 1970-01-01
      • 2016-07-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多