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 [email protected] protocol type 'MyProtocol'
   let conforms = T.self is MyProtocol
                  ~~~~~~ ^  ~~~~~~~~~~

Я также пробовал варианты, такие как T.self is MyProtocol.self, T is MyProtocol и используя == вместо is. До сих пор я никуда не денусь. Любые идеи?

Ответ 1

Немного поздно, но вы можете проверить, реагирует ли что-то на протокол с тегом as ?:

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

EDIT: немного короче:

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

И используя охрану

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

Ответ 2

Самый простой ответ: не делайте этого. Вместо этого используйте перегрузку и ограничения, а во время компиляции все время проверяйте, а не тестируйте материал динамически во время выполнения. Проверки типа времени выполнения и обобщенные дженерики похожи на стейк и мороженое - оба хороши, но смешение их немного странно.

Рассмотрим примерно следующее:

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

Я должен сказать, что @Alex хочет проверить, соответствует ли тип T протоколу, а не s. И какой-то ответчик не видел ясно.

Проверьте тип T, соответствующий этому протоколу:

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

или

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

Ответ 4

вы также можете использовать быстрое соответствие шаблону case case, если вы хотите обрабатывать несколько случаев типа T:

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

Вам нужно объявить протокол как @objc:

@objc protocol MyProtocol {
    ...
} 

Из книги Apple "Язык быстрого программирования":

Вы можете проверить соответствие протокола только в том случае, если ваш протокол отмечен атрибутом @objc, как показано выше для протокола HasArea. Этот атрибут указывает, что протокол должен быть открыт с кодом Objective-C и описан в разделе Использование Swift с Cocoa и Objective-C. Даже если вы не взаимодействуете с Objective-C, вам нужно пометить свои протоколы атрибутом @objc, если вы хотите проверить соответствие протокола.

Обратите внимание, что протоколы @objc могут быть приняты только классами, а не структурами или перечислениями. Если вы отметите свой протокол как @objc, чтобы проверить соответствие, вы сможете применить этот протокол только к типам классов.