web-dev-qa-db-ja.com

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の代わりに==を使用しました。これまでのところ、私はどこにも行きませんでした。何か案は?

61
Alex

少し遅れますが、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
}
60
jlngdt

@Alexは、T型ではなく、s型がプロトコルに準拠しているかどうかを確認する必要があります。そして、一部の回答者ははっきりと見ませんでした。

Tタイプが次のようなプロトコルに準拠していることを確認します。

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

または

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

最も簡単な答えは、そうしないことです。代わりにオーバーロードと制約を使用し、実行時に動的にテストするのではなく、コンパイル時にすべてを事前に決定します。実行時の型チェックとコンパイル時のジェネリックはステーキやアイスクリームのようなものです。両方ともニースで​​すが、それらを混ぜることは少し奇妙です。

次のようなものを検討してください。

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

しかし、正直なところ、ジェネリック関数内で本当にそれを行う必要がありますか?実際の型が何か分からない場合は、それを後回しにして汎用関数内に挿入して調べるのではなく、事前に把握することをお勧めします。

31

タイプTの複数のケースを処理する場合は、Swiftの 大文字と小文字を切り替えるパターンマッチング を活用することもできます。

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

プロトコルを@objcとして宣言する必要があります。

@objc protocol MyProtocol {
    ...
} 

Appleの「The Swift Programming Language」ブックから:

上記のHasAreaプロトコルで見られるように、プロトコルが@objc属性でマークされている場合にのみ、プロトコルの適合性を確認できます。この属性は、プロトコルをObjective-Cコードに公開する必要があることを示しており、CocoaおよびObjective-CでのSwiftの使用で説明されています。 Objective-Cと相互運用していない場合でも、プロトコルの適合性を確認できるようにするには、@ objc属性でプロトコルをマークする必要があります。

また、@ objcプロトコルはクラスでのみ採用でき、構造や列挙では採用できないことに注意してください。適合性をチェックするためにプロトコルを@objcとしてマークすると、そのプロトコルをクラスタイプにのみ適用できます。

5
rabbitinspace

let conforms = T.self is MyProtocol.Type

3