Beberapa Jenis Kendala dalam Swift

133

Katakanlah saya memiliki protokol ini:

protocol SomeProtocol {

}

protocol SomeOtherProtocol {

}

Sekarang, jika saya ingin fungsi yang menggunakan tipe generik, tetapi tipe itu harus sesuai dengan yang SomeProtocolbisa saya lakukan:

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

Tetapi apakah ada cara untuk menambahkan batasan tipe untuk banyak protokol?

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

}

Hal serupa menggunakan koma, tetapi dalam kasus ini, ia akan memulai deklarasi dari tipe yang berbeda. Inilah yang saya coba.

<T: SomeProtocol | SomeOtherProtocol>
<T: SomeProtocol , SomeOtherProtocol>
<T: SomeProtocol : SomeOtherProtocol>
Logan
sumber
Ini adalah pertanyaan khusus yang relevan karena dokumen Swift tidak menyebutkan ini dalam bab generik ...
Bruno Philipe

Jawaban:

241

Anda dapat menggunakan klausa di mana yang memungkinkan Anda menentukan sebanyak mungkin persyaratan yang Anda inginkan (semuanya harus dipenuhi) dipisahkan dengan koma

Swift 2:

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

Swift 3 & 4:

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

atau klausa mana yang lebih kuat:

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

Anda tentu saja dapat menggunakan komposisi protokol (misalnya, protocol<SomeProtocol, SomeOtherProtocol>), tetapi sedikit kurang fleksibel.

Menggunakan wherememungkinkan Anda menangani kasus-kasus di mana banyak jenis terlibat.

Anda mungkin masih ingin membuat protokol untuk digunakan kembali di banyak tempat, atau hanya untuk memberikan protokol yang lengkap nama yang bermakna.

Swift 5:

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

Ini terasa lebih alami karena protokol di sebelah argumen.

Jiaaro
sumber
Ya ampun ini tidak masuk akal, tetapi senang mengetahui bahwa saya hanya ingin menjadi salah satu spammer terima kasih untuk yang ini, belum menyadari ini dalam sebulan sejak saya membutuhkannya.
Mathijs Segers
3
Adakah cara untuk melakukan hal yang sama dengan kelas dan struct dalam ekspresi tipe contraint? misalnya <T where T:SomeStruct, T:AnotherStruct>? Untuk kelas kompiler tampaknya menafsirkan ini dengan mengatakan "T adalah subkelas dari keduanya", dan untuk struct hanya mengeluh itu "Type 'T' constrained to non-protocol type".
Jarrod Smith
Untuk contoh spesifik dalam OP: s pertanyaan komposisi protokol harus menjadi metode lebih: solusi di atas adalah valid, tapi, imho, mengacaukan tidak perlu fungsi tanda tangan. Juga, menggunakan komposisi protokol sebagai, misalnya, batasan tipe, masih memungkinkan Anda menggunakan whereklausa untuk tipe tambahan / penggunaan lainnya, misalnya func someFunc<U, T: protocol<SomeProtocol, SomeOtherProtocol> where T.SubType == U>(arg: T, arg2: U) { ... }untuk typealias SubTypedi misalnya SomeProtocol.
dfri
1
Sepertinya ini sudah usang di swift3 dan merekomendasikan untuk menggunakan: func someFunc <T> (arg: T) di mana T: SomeProtocol, T: SomeOtherProtocol {
Cristi Băluță
2
Apakah ada cara untuk mengatakan dengan cepat bahwa T harus dari tipe objek tertentu DAN mengimplementasikan protokol tertentu?
Georg
73

Anda memiliki dua kemungkinan:

  1. Anda menggunakan klausa tempat seperti yang ditunjukkan dalam jawaban Jiaaro:

    func someFunc<T where T : SomeProtocol, T : SomeOtherProtocol>(arg: T) {
        // do stuff
    }
    
  2. Anda menggunakan jenis komposisi protokol :

    func someFunc<T : protocol<SomeProtocol, SomeOtherProtocol>>(arg: T) {
        // do stuff
    }
    
Jean-Philippe Pellet
sumber
2
imo solusi kedua lebih cantik, saya akan pergi untuk jawaban ini juga lebih lengkap menyajikan dua opsi
Mathijs Segers
2
Nomor 2 adalah satu-satunya yang bekerja untuk saya di bawah Swift 2 ketika mendeklarasikan a typealias. Terima kasih!
Bruno Philipe
19

Evolusi ke Swift 3.0 membawa beberapa perubahan. Dua pilihan kami sekarang terlihat sedikit berbeda.

Menggunakan whereklausa di Swift 3.0:

The whereklausul sekarang pindah ke akhir tanda tangan fungsi untuk meningkatkan keterbacaan. Jadi beberapa protokol warisan sekarang terlihat seperti ini:

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

}

Menggunakan protocol<>konstruk di Swift 3.0:

Komposisi menggunakan protocol<>konstruk tidak lagi digunakan. Sebelumnya protocol<SomeProtocol, SomeOtherProtocol>sekarang terlihat seperti ini:

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

}

Referensi.

Info lebih lanjut tentang perubahan whereada di sini: https://github.com/apple/swift-evolution/blob/master/proposals/0081-move-where-expression.md

Dan, lebih lanjut tentang perubahan protokol <> konstruk ada di sini: https://github.com/apple/swift-evolution/blob/master/proposals/0095-any-as-existential.md

ncke
sumber
13

Swift 3 menawarkan hingga 3 cara berbeda untuk mendeklarasikan fungsi Anda.

protocol SomeProtocol {
    /* ... */
}

protocol SomeOtherProtocol {
    /* ... */        
}

1. Menggunakan &operator

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

2. Menggunakan whereklausa

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

3. Menggunakan whereklausa dan &operator

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

Perhatikan juga bahwa Anda dapat menggunakan typealiasuntuk mempersingkat deklarasi fungsi Anda.

typealias RequiredProtocols = SomeProtocol & SomeOtherProtocol

func someFunc<T: RequiredProtocols>(arg: T) {
    /* ... */   
}
Imanou Petit
sumber