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, чтобы проверить соответствие, вы сможете применить этот протокол только к типам классов.