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

96

我有一个协议,定义如下:

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个回答

101
我得说@Alex想要检查的是T类型是否符合协议,而不是s。有些回答者看不清楚。
像这样检查T类型是否符合协议:
if let _ = T.self as? MyProtocol.Type {
    //  T conform MyProtocol
}

或者

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

27
如果我的协议 MyProtocol 是一个带有关联类型的 CaseIterable 协议,会出现错误:"'CaseIterable' 只能用作泛型约束,因为它具有 Self 或关联类型要求。" - zgjie
@zgjie,你找到评论的答案了吗? - dev_ios999

85

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

9
需要一个T的实例,但问题要求关于泛型类型。因此,Carlos的回答更好:https://dev59.com/p14c5IYBdhLWcg3wQoY5#52787263 - Sajjon
Carlos Chaguendo 的回答应该是被接受的答案,我个人认为。 - J.beenie
这并没有回答问题。 - Jonathan.
是的,它并没有按照所问的问题进行回答,但它回答了_我的_问题。 - Nolan Amy

40
最简单的答案是:不要那样做。使用重载和约束,将所有内容在编译时确定而不是在运行时动态测试。在运行时进行类型检查和在编译时使用泛型就像吃牛排和冰淇淋 - 两者都不错,但混在一起有点奇怪。
考虑下面的示例:
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

但是,说实话,你真的需要在通用函数内执行这个操作吗?如果你不确定某个实际类型是什么,更好的选择可能是提前弄清楚,而不是将其推迟到稍后并在通用函数内查找。


19
+1,好的回答。特别喜欢“运行时类型检查和编译时泛型就像牛排和冰淇淋一样-两者都很美味,但混合在一起有点奇怪。” - Stuart
9
是的……除了那些过载和限制无法完成工作的边缘情况。考虑一个实现协议JSONEncodable的扩展方法,该协议要求init(json: JSON) throws。我们希望Array实现JSONEncodable,但是只有当其元素也是JSONEncodable时才实现。我们无法将继承子句与约束条件组合在一起,因此我们必须在init的实现中使用某种类型检查,并且如果Element不是JSONEncodable,可能会引发错误。可惜根据我的了解,这似乎不可能实现。 - Gregory Higley
我应该补充说明,上述难题可以通过使用一个中间类型作为thunk来解决,但这是一个相当不优雅的解决方案。 - Gregory Higley
@GregoryHigley,现在在Swift 4.1中可以通过条件一致性实现这个可能性(https://swift.org/blog/conditional-conformance/)。 - mj_jimenez
很酷的是,你可以使用或不使用像<Type>这样的提示来构造MyStruct,它会自动判断该怎么做。对于其他尝试运行代码的人来说,Swift 4需要在第一个构造函数参数上加上_ - snakeoil

24

让conforms等于T.self是否是MyProtocol.Type类型


3
这是最佳答案,因为它回答了原始问题“如何实现:let conforms = T.self is MyProtocol”。 - Leslie Godwin
1
同意,这是问题的最佳答案! - Jordi Puigdellívol
不支持与associatedType协议(泛型)一起使用。 - JAHelia

12

如果您想处理类型 T 的多个情况,您还可以利用 Swift 的switch case 模式匹配

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
    ...
    }
}

这也不能回答问题,因为它测试的是实例是否符合协议,而不是直接测试类型。 - Jonathan.

6

对于测试用例,我会这样检查符合性:

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

6
您需要将协议声明为@objc
@objc protocol MyProtocol {
    ...
} 

从苹果公司的“Swift编程语言”书中得知: 只有标记了@objc属性的协议才能检查其符合性,就像上面的HasArea协议所示。这个属性表明该协议应该暴露给Objective-C代码,并在“使用Swift与Cocoa和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
1
@Alex,在检查协议一致性之前,您需要构造T类型的实例(据我所知)。 如果您需要T类型仅为符合MyProtocol的类型,则可以指定:func myFunc<T: MyProtocol>(...) -> T? - rabbitinspace

-4
现代的答案可能是这样的:(Swift 5.1)
func myFunc < T: MyProtocol> (s: MyStruct<T>) -> T? {    ... }

9
那怎样回答这个问题呢? - Peter Schorn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接