Обмеження декількох типів у Swift


133

Скажімо, у мене є такі протоколи:

protocol SomeProtocol {

}

protocol SomeOtherProtocol {

}

Тепер, якщо я хочу функцію, яка приймає загальний тип, але цей тип повинен відповідати, SomeProtocolя можу зробити:

func someFunc<T: SomeProtocol>(arg: T) {
    // do stuff
}

Але чи є спосіб додати обмеження типу для декількох протоколів?

func bothFunc<T: SomeProtocol | SomeOtherProtocol>(arg: T) {

}

У подібних речах використовуються коми, але в цьому випадку воно розпочнеть декларацію іншого типу. Ось що я спробував.

<T: SomeProtocol | SomeOtherProtocol>
<T: SomeProtocol , SomeOtherProtocol>
<T: SomeProtocol : SomeOtherProtocol>

Це особливо актуальне питання, оскільки документи Свіфта не згадують про це у розділі генерики ...
Бруно Філіпе

Відповіді:


241

Ви можете використовувати пункт де, який дозволяє вказати стільки вимог, скільки потрібно (усі вони повинні бути виконані), розділених комами

Швидкий 2:

func someFunc<T where T:SomeProtocol, T:SomeOtherProtocol>(arg: T) {
    // stuff
}

Swift 3 і 4:

func someFunc<T: SomeProtocol & SomeOtherProtocol>(arg: T) {
    // stuff
}

або найпотужніший де пункт:

func someFunc<T>(arg: T) where T:SomeProtocol, T:SomeOtherProtocol{
    // stuff
}

Можна, звичайно, використовувати склад протоколу (наприклад, protocol<SomeProtocol, SomeOtherProtocol>), але це трохи менш гнучко.

Використання whereдозволяє вирішувати випадки, коли задіяно кілька типів.

Ви все ще можете скласти протоколи для повторного використання в декількох місцях або просто дати складеному протоколу значущу назву.

Швидкий 5:

func someFunc(arg: SomeProtocol & SomeOtherProtocol) { 
    // stuff
}

Це здається більш природним, оскільки протоколи поряд з аргументом.


Гейз, це не логічно, але добре знати, що я просто хочу бути одним із спамерів подяки за це, не зрозумів це через місяць, як мені це було потрібно.
Mathijs Segers

3
Будь-який спосіб зробити те ж саме з класами та структурами у виразі контрастного типу? наприклад <T where T:SomeStruct, T:AnotherStruct>? Для класів, здається, компілятор інтерпретує це як висловлювання "T - підклас обох", а для структур він просто скаржиться на це "Type 'T' constrained to non-protocol type".
Jarrod Smith

Для конкретного прикладу в складі протоколу питання OP: s слід віддати перевагу методу: рішення, вказане вище, є дійсним, але, тим не менш, непотрібно захаращує функцію підпису функції. Також, використовуючи склад протоколу, як, наприклад, обмеження типу, усе ще дозволяє використовувати whereпункт для додаткового типу / іншого використання, наприклад func someFunc<U, T: protocol<SomeProtocol, SomeOtherProtocol> where T.SubType == U>(arg: T, arg2: U) { ... }для typealias SubTypeв напр SomeProtocol.
dfri

1
Схоже, це застаріле в swift3 і рекомендує використовувати: func someFunc <T> (arg: T) де T: SomeProtocol, T: SomeOtherProtocol {
Cristi Bălu Bă

2
Чи є спосіб стрімко сказати, що T має бути певного типу об'єкта та реалізувати певний протокол?
Георг

73

У вас є дві можливості:

  1. Ви використовуєте пункт де, як зазначено у відповіді Цзяаро:

    func someFunc<T where T : SomeProtocol, T : SomeOtherProtocol>(arg: T) {
        // do stuff
    }
    
  2. Ви використовуєте тип композиції протоколу :

    func someFunc<T : protocol<SomeProtocol, SomeOtherProtocol>>(arg: T) {
        // do stuff
    }
    

2
тому що друге рішення буде гарнішим, я б пішов на цю відповідь, але також є більш повною, представляючи два варіанти
Mathijs Segers

2
Число 2 - це єдине, що працює для мене під Swift 2 при оголошенні a typealias. Дякую!
Бруно Філіпе

19

Еволюція до Swift 3.0 приносить деякі зміни. Наші два варіанти виглядають дещо інакше.

Використання whereпункту в Swift 3.0:

Розділ whereтепер переміщено до кінця підпису функції для поліпшення читабельності. Тож багаторазове успадкування протоколу зараз виглядає так:

func someFunc<T>(arg: T) where T:SomeProtocol, T:SomeOtherProtocol {

}

Використання protocol<>конструкції в Swift 3.0:

Композиція з використанням protocol<>конструкції застаріла. Раніше protocol<SomeProtocol, SomeOtherProtocol>зараз виглядає так:

func someFunc<T:SomeProtocol & SomeOtherProtocol>(arg: T) {

}

Список літератури.

Більше інформації про зміни можна whereзнайти тут: https://github.com/apple/swift-evolution/blob/master/proposals/0081-move-where-expression.md

Більше про зміни для протоколу <> construct читайте тут: https://github.com/apple/swift-evolution/blob/master/proposals/0095-any-as-existential.md


13

Swift 3 пропонує до 3 різних способів заявити про свою функцію.

protocol SomeProtocol {
    /* ... */
}

protocol SomeOtherProtocol {
    /* ... */        
}

1. Використання &оператора

func someFunc<T: SomeProtocol & SomeOtherProtocol>(arg: T) {
    /* ... */
}

2. Використання whereпункту

func someFunc<T>(arg: T) where T: SomeProtocol, T: SomeOtherProtocol {
    /* ... */
}

3. Використання whereпункту та &оператора

func someFunc<T>(arg: T) where T: SomeProtocol & SomeOtherProtocol {
    /* ... */        
}

Також зауважте, що ви можете використовувати їх typealiasдля скорочення декларації функції.

typealias RequiredProtocols = SomeProtocol & SomeOtherProtocol

func someFunc<T: RequiredProtocols>(arg: T) {
    /* ... */   
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.