Swift'de Çoklu Tip Kısıtlamaları


133

Diyelim ki şu protokollere sahibim:

protocol SomeProtocol {

}

protocol SomeOtherProtocol {

}

Şimdi, genel bir tür alan bir işlev istiyorsam, ancak bu türün uyması gerekiyorsa SomeProtocolyapabilirim:

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

Ancak birden çok protokol için tür kısıtlaması eklemenin bir yolu var mı?

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

}

Benzer şeyler virgül kullanır, ancak bu durumda farklı bir tür beyanı başlatır. İşte denediğim şey.

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

Swift dokümanları jenerikler bölümünde bundan bahsetmediği için bu özellikle alakalı bir sorudur ...
Bruno Philipe

Yanıtlar:


242

Virgülle ayırarak istediğiniz kadar çok gereksinimi (tümü yerine getirilmelidir) belirtmenize izin veren bir where cümlesi kullanabilirsiniz.

Swift 2:

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

Hızlı 3 ve 4:

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

veya daha güçlü where cümlesi:

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

Elbette protokol bileşimini kullanabilirsiniz (örneğin protocol<SomeProtocol, SomeOtherProtocol>), ancak biraz daha az esnektir.

Kullanmak where, birden fazla türün dahil olduğu vakalarla ilgilenmenize olanak tanır.

Yine de birden fazla yerde yeniden kullanılmak üzere protokoller oluşturmak veya sadece oluşturulan protokole anlamlı bir ad vermek isteyebilirsiniz.

Swift 5:

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

Protokoller argümanın yanında olduğu için bu daha doğal geliyor.


Tanrım, bu mantıklı değil, ancak bunun için teşekkür spam göndericilerinden biri olmak istediğimi bilmek güzel, 'bunu ihtiyacımdan bu yana bir ay içinde fark etmedim.
Mathijs Segers

3
Tür kontraint ifadesinde sınıflar ve yapılar için aynı şeyi yapmanın bir yolu var mı? örneğin <T where T:SomeStruct, T:AnotherStruct>? Sınıflar için derleyici bunu "T her ikisinin alt sınıfıdır" şeklinde yorumluyor gibi görünür ve yapılar için bundan şikayet eder "Type 'T' constrained to non-protocol type".
Jarrod Smith

OP'nin soru protokol kompozisyonundaki spesifik örnek için tercih edilen yöntem olmalıdır : yukarıdaki çözüm geçerlidir, ancak imho, gereksiz yere işlev imzasını karıştırır. Ayrıca, örneğin, bir tür sınırlaması protokol kompozisyonunu kullanarak, hala kullanmanızı sağlar whereek tip / diğer kullanımı, örneğin için maddeyi func someFunc<U, T: protocol<SomeProtocol, SomeOtherProtocol> where T.SubType == U>(arg: T, arg2: U) { ... }typealias için SubTypeörneğin içinde SomeProtocol.
dfri

1
Görünüşe göre bu, swift3'te kullanımdan kaldırılmıştır ve: func someFunc <T> (arg: T) kullanılmasını önerir; burada T: SomeProtocol, T: SomeOtherProtocol {
Cristi Băluță

2
Swift'e T'nin belirli bir nesne türünde olması ve belirli bir protokolü uygulaması gerektiğini söylemenin bir yolu var mı?
Georg

73

İki seçeneğiniz var:

  1. Jiaaro'nun cevabında belirtildiği gibi bir where cümlesi kullanıyorsunuz :

    func someFunc<T where T : SomeProtocol, T : SomeOtherProtocol>(arg: T) {
        // do stuff
    }
  2. Bir protokol oluşturma türü kullanıyorsunuz :

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

2
imo ikinci çözüm daha güzel, bu cevaba gideceğim, ayrıca iki seçeneği sunmak daha eksiksiz
Mathijs Segers

2
2 Numara, Swift 2 altında a typealias. Teşekkürler!
Bruno Philipe

19

Swift 3.0'daki evrim bazı değişiklikler getiriyor. İki seçeneğimiz şimdi biraz farklı görünüyor.

whereSwift 3.0'da bir cümle kullanmak :

Yan wheretümce, okunabilirliği artırmak için artık bir işlev imzasının sonuna taşınmıştır. Yani çoklu protokol kalıtımı artık şu şekilde görünüyor:

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

}

Kullanma protocol<>Swift 3.0 yapısı:

Yapıyı kullanan kompozisyon protocol<>kullanımdan kaldırılıyor. Daha önce protocol<SomeProtocol, SomeOtherProtocol>şuna benziyor:

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

}

Referanslar.

İçin değişiklikler hakkında daha fazla bilgi whereburada: https://github.com/apple/swift-evolution/blob/master/proposals/0081-move-where-expression.md

Ve <> protokol yapısındaki değişiklikler hakkında daha fazla bilgi burada: https://github.com/apple/swift-evolution/blob/master/proposals/0095-any-as-existential.md


13

Swift 3, işlevinizi açıklamanın 3 farklı yolunu sunar.

protocol SomeProtocol {
    /* ... */
}

protocol SomeOtherProtocol {
    /* ... */        
}

1. &Operatörü kullanma

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

2. wheremadde kullanma

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

3. whereMadde ve &operatör kullanma

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

Ayrıca typealiasişlev bildiriminizi kısaltmak için kullanabileceğinizi unutmayın .

typealias RequiredProtocols = SomeProtocol & SomeOtherProtocol

func someFunc<T: RequiredProtocols>(arg: T) {
    /* ... */   
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.