ข้อ จำกัด หลายประเภทใน Swift


135

สมมติว่าฉันมีโปรโตคอลเหล่านี้:

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>

นี่เป็นคำถามที่เกี่ยวข้องเป็นพิเศษเนื่องจากเอกสาร Swift ไม่ได้กล่าวถึงสิ่งนี้ในบททั่วไป ...
Bruno Philipe

คำตอบ:


250

คุณสามารถใช้คำสั่ง whereซึ่งช่วยให้คุณระบุข้อกำหนดได้มากเท่าที่คุณต้องการ (ซึ่งทั้งหมดจะต้องเป็นไปตามนั้น) คั่นด้วยลูกน้ำ

Swift 2:

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

Swift 3 และ 4:

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

หรือมีประสิทธิภาพมากขึ้นโดยที่ clause:

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

แน่นอนคุณสามารถใช้องค์ประกอบโปรโตคอล (เช่นprotocol<SomeProtocol, SomeOtherProtocol>) แต่มีความยืดหยุ่นน้อยกว่าเล็กน้อย

การใช้whereช่วยให้คุณจัดการกับกรณีที่เกี่ยวข้องกับหลายประเภท

คุณอาจยังต้องการสร้างโปรโตคอลเพื่อใช้ซ้ำในหลาย ๆ ที่หรือเพียงเพื่อตั้งชื่อที่มีความหมายให้โปรโตคอลที่สร้างขึ้น

สวิฟต์ 5:

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

สิ่งนี้ให้ความรู้สึกเป็นธรรมชาติมากขึ้นเนื่องจากโปรโตคอลอยู่ถัดจากการโต้แย้ง


Geez นี่ไม่ใช่เหตุผล แต่ก็ดีที่รู้ว่าฉันแค่อยากเป็นหนึ่งในสแปมเมอร์ขอบคุณสำหรับผู้ส่งอีเมลรายนี้ไม่ได้ตระหนักถึงสิ่งนี้ภายในหนึ่งเดือนนับตั้งแต่ฉันต้องการมัน
Mathijs Segers

3
มีวิธีใดบ้างที่จะทำสิ่งเดียวกันกับคลาสและโครงสร้างในประเภท contraint expression? เช่น<T where T:SomeStruct, T:AnotherStruct>? สำหรับคลาสคอมไพลเลอร์ดูเหมือนจะตีความสิ่งนี้ว่า "T เป็นคลาสย่อยของทั้งสอง" และสำหรับโครงสร้างมันก็บ่น"Type 'T' constrained to non-protocol type"อย่างนั้น
Jarrod Smith

สำหรับตัวอย่างเฉพาะในองค์ประกอบของโปรโตคอลคำถาม OP: ควรเป็นวิธีที่ดีกว่า: วิธีแก้ปัญหาข้างต้นถูกต้อง แต่ imho จะถ่วงลายเซ็นฟังก์ชัน นอกจากนี้การใช้องค์ประกอบโปรโตคอลเป็นเช่นประเภท จำกัด ยังคงช่วยให้คุณใช้whereประโยคสำหรับประเภทเพิ่มเติม / การใช้งานอื่น ๆ เช่นfunc someFunc<U, T: protocol<SomeProtocol, SomeOtherProtocol> where T.SubType == U>(arg: T, arg2: U) { ... }สำหรับ typealias ในเช่นSubType SomeProtocol
dfrib

1
ดูเหมือนว่าสิ่งนี้จะเลิกใช้แล้วใน swift3 และแนะนำให้ใช้: func someFunc <T> (arg: T) โดยที่ T: SomeProtocol, T: SomeOtherProtocol {
Cristi Băluță

2
มีวิธีบอกอย่างรวดเร็วว่า T ต้องเป็นวัตถุบางประเภทและใช้โปรโตคอลบางอย่างหรือไม่?
Georg

74

คุณมีความเป็นไปได้สองประการ:

  1. คุณใช้where clauseตามที่ระบุไว้ในคำตอบของ Jiaaro:

    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 เมื่อประกาศกtypealias. ขอบคุณ!
Bruno Philipe

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

และข้อมูลเพิ่มเติมเกี่ยวกับการเปลี่ยนแปลงของโครงสร้างโปรโตคอล <> อยู่ที่นี่: 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) {
    /* ... */   
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.