Swift:检查泛型类型是否符合协议

时间:2015-01-24 10:51:15

标签: generics swift

我有一个我这样定义的协议:

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

8 个答案:

答案 0 :(得分:55)

有点晚了但你可以用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
}

答案 1 :(得分:49)

我不得不说@Alex想检查T类型是否符合协议而不是s。一些回答者没有看清楚。

检查T类型是否符合以下协议:

if let _ = T.self as? MyProtocol.Type {
    //  T conform MyProtocol
}

if T.self is MyProtocol.Type {
    //  T conform MyProtocol
}

答案 2 :(得分:31)

最简单的答案是:不要这样做。使用重载和约束,并在编译时预先确定所有内容,而不是在运行时动态测试内容。运行时类型检查和编译时泛型就像牛排和冰淇淋 - 两者都很好但混合它们有点奇怪。

考虑这样的事情:

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

但是,老实说,你真的需要在你的通用功能中做到这一点吗?如果您不确定某些实际类型是什么,那么更好的选择可能是预先计算出来而不是将其推迟到以后并在通用函数内进行刺激以找出它。

答案 3 :(得分:10)

如果你想处理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
    ...
    }
}

答案 4 :(得分:4)

您需要将协议声明为@objc

@objc protocol MyProtocol {
    ...
} 

来自Apple的“The Swift Programming Language”一书:

  

只有当您的协议标有@objc属性时才能检查协议一致性,如上面的HasArea协议所示。此属性指示协议应该暴露给Objective-C代码,并在使用Swift with Cocoa和Objective-C中进行了描述。即使您没有与Objective-C进行互操作,如果您希望能够检查协议一致性,还需要使用@objc属性标记协议。

     

另请注意,@ objc协议只能由类采用,而不能由结构或枚举采用。如果将协议标记为@objc以检查一致性,则只能将该协议应用于类类型。

答案 5 :(得分:1)

让我符合= T.self是MyProtocol.Type

答案 6 :(得分:1)

一个现代的答案将是这样的:(Swift 5.1)

func myFunc < T: MyProtocol> (s: MyStruct<T>) -> T? {    ... }

答案 7 :(得分:0)

对于测试用例,我像这样检查一致性:

let conforms: Bool = (Controller.self as Any) is Protocol.Type