简体   繁体   English

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

[英]Swift: check if generic type conforms to protocol

I have a protocol that I defined like so:我有一个我这样定义的协议:

protocol MyProtocol {
   ...
}

I also have a generic struct:我也有一个通用结构:

struct MyStruct <T>  {
    ...
}

Finally I have a generic function:最后我有一个通用函数:

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

I'd like to test inside of the function if the type T conforms to MyProtocol.如果类型 T 符合 MyProtocol,我想在函数内部进行测试。 Essentially I'd like to be able to do (~ pseudocode):基本上我希望能够做到(~伪代码):

let conforms = T.self is MyProtocol

But this throws a compiler error:但这会引发编译器错误:

error: cannot downcast from 'T.Type' to non-@objc protocol type 'MyProtocol'
   let conforms = T.self is MyProtocol
                  ~~~~~~ ^  ~~~~~~~~~~

I have also tried variations, like T.self is MyProtocol.self , T is MyProtocol , and using == instead of is .我也尝试过变体,比如T.self is MyProtocol.selfT is MyProtocol ,并使用==而不是is So far I haven't gotten anywhere.到目前为止,我还没有到任何地方。 Any ideas?有任何想法吗?

I have to say @Alex want to check if T type conforms to protocol rather than s .我不得不说@Alex 想检查T类型是否符合协议而不是s And some answerer didn't see clearly.有的答主没看清楚。

Check T type conforms to protocol like this :检查T类型符合这样的协议:

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

or或者

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

A bit late but you can test if something responds to protocol with as ?有点晚了,但你可以测试是否有东西响应协议as ? test:测试:

if let currentVC = myViewController as? MyCustomProtocol {
    // currentVC responds to the MyCustomProtocol protocol =]
}

EDIT: a bit shorter:编辑:短一点:

if let _ = self as? MyProtocol {
    // match
}

And using a guard:并使用警卫:

guard let _ = self as? MyProtocol else {
    // doesn't match
    return
}

The simplest answer is: don't do that.最简单的答案是:不要那样做。 Use overloading and constraints instead, and determine everything up-front at compile-time rather than testing stuff dynamically at runtime.改用重载和约束,并在编译时预先确定所有内容,而不是在运行时动态测试内容。 Runtime type checking and compile-time generics are like steak and ice-cream – both are nice but mixing them is a bit weird.运行时类型检查和编译时泛型就像牛排和冰淇淋——两者都很好,但混合它们有点奇怪。

Consider something like this:考虑这样的事情:

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

The downside being, you can't establish dynamically if T supports a protocol at runtime:缺点是,如果 T 在运行时支持协议,则无法动态建立:

let o: Any = S1()
let m3 = MyStruct(val: o)
myFunc(m3)  // will return nil even though o 
            // does actually implement MyProtocol

But, in all honesty, do you really need to do that inside your generic function?但是,老实说,你真的需要在你的泛型函数中这样做吗? If you're unsure what actual type something is, the better option may be to figure that out up-front rather than deferring it to later and prodding it inside a generic function to find out.如果您不确定某事物的实际类型是什么,更好的选择可能是预先弄清楚,而不是推迟到以后并在通用函数中刺激它以找出答案。

让符合 = T.self 是 MyProtocol.Type

you can also leverage swift's switch case pattern matching , if you want to handle multiple cases of type T :如果您想处理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
    ...
    }
}

You need declare protocol as @objc :您需要将协议声明为@objc

@objc protocol MyProtocol {
    ...
} 

From Apple's "The Swift Programming Language" book:来自 Apple 的“The Swift Programming Language”一书:

You can check for protocol conformance only if your protocol is marked with the @objc attribute, as seen for the HasArea protocol above.仅当您的协议标有 @objc 属性时,您才能检查协议一致性,如上面的 HasArea 协议所示。 This attribute indicates that the protocol should be exposed to Objective-C code and is described in Using Swift with Cocoa and Objective-C.该属性表明协议应该暴露给 Objective-C 代码,在使用 Swift 与 Cocoa 和 Objective-C 中进行了描述。 Even if you are not interoperating with Objective-C, you need to mark your protocols with the @objc attribute if you want to be able to check for protocol conformance.即使您不与 Objective-C 互操作,如果您希望能够检查协议一致性,也需要使用 @objc 属性标记您的协议。

Note also that @objc protocols can be adopted only by classes, and not by structures or enumerations.还要注意@objc 协议只能被类采用,不能被结构或枚举采用。 If you mark your protocol as @objc in order to check for conformance, you will be able to apply that protocol only to class types.如果您将协议标记为 @objc 以检查一致性,您将只能将该协议应用于类类型。

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

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

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM