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

时间:2020-12-29 16:09:54

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. Essentially I'd like to be able to do (~ pseudocode):

如果类型T符合MyProtocol,我想在函数内部进行测试。基本上我想做(~伪代码):

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. So far I haven't gotten anywhere. Any ideas?

我也试过变奏,比如T。自我是MyProtocol。self, T是MyProtocol,使用==而不是。到目前为止我还没有任何进展。什么好主意吗?

5 个解决方案

#1


47  

A bit late but you can test if something responds to protocol with as ? test:

有点晚了,但是你可以测试一些东西是否响应了as协议?测试:

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
}

#2


28  

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.

但是,老实说,你真的需要在你的通用函数中这样做吗?如果您不确定实际的类型是什么,那么更好的选择可能是预先确定,而不是将其推迟到以后,并在泛型函数中推动它来查找。

#3


27  

I have to say @Alex want to check if T type conforms to protocol rather than s. And some answerer didn't see clearly.

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

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
}

#4


10  

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

#5


4  

You need declare protocol as @objc:

您需要将协议声明为@objc:

@objc protocol MyProtocol {
    ...
} 

From Apple's "The Swift Programming Language" book:

从苹果的“Swift编程语言”一书中:

You can check for protocol conformance only if your protocol is marked with the @objc attribute, as seen for the HasArea protocol above. This attribute indicates that the protocol should be exposed to Objective-C code and is described in Using Swift with Cocoa and 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.

只有当您的协议被标记为@objc属性时,您才能检查协议的一致性,如上面的HasArea协议所示。此属性指示协议应该公开给Objective-C代码,并在使用Swift与Cocoa和Objective-C时进行描述。即使您没有与Objective-C进行交互,如果您想检查协议是否符合的话,也需要使用@objc属性标记您的协议。

Note also that @objc protocols can be adopted only by classes, and not by structures or enumerations. 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协议只能被类使用,不能被结构或枚举使用。如果您将您的协议标记为@objc以检查一致性,那么您将只能将该协议应用于类类型。

#1


47  

A bit late but you can test if something responds to protocol with as ? test:

有点晚了,但是你可以测试一些东西是否响应了as协议?测试:

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
}

#2


28  

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.

但是,老实说,你真的需要在你的通用函数中这样做吗?如果您不确定实际的类型是什么,那么更好的选择可能是预先确定,而不是将其推迟到以后,并在泛型函数中推动它来查找。

#3


27  

I have to say @Alex want to check if T type conforms to protocol rather than s. And some answerer didn't see clearly.

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

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
}

#4


10  

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

#5


4  

You need declare protocol as @objc:

您需要将协议声明为@objc:

@objc protocol MyProtocol {
    ...
} 

From Apple's "The Swift Programming Language" book:

从苹果的“Swift编程语言”一书中:

You can check for protocol conformance only if your protocol is marked with the @objc attribute, as seen for the HasArea protocol above. This attribute indicates that the protocol should be exposed to Objective-C code and is described in Using Swift with Cocoa and 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.

只有当您的协议被标记为@objc属性时,您才能检查协议的一致性,如上面的HasArea协议所示。此属性指示协议应该公开给Objective-C代码,并在使用Swift与Cocoa和Objective-C时进行描述。即使您没有与Objective-C进行交互,如果您想检查协议是否符合的话,也需要使用@objc属性标记您的协议。

Note also that @objc protocols can be adopted only by classes, and not by structures or enumerations. 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协议只能被类使用,不能被结构或枚举使用。如果您将您的协议标记为@objc以检查一致性,那么您将只能将该协议应用于类类型。