Nhiều ràng buộc kiểu trong Swift


133

Hãy nói rằng tôi có các giao thức này:

protocol SomeProtocol {

}

protocol SomeOtherProtocol {

}

Bây giờ, nếu tôi muốn một hàm có kiểu chung, nhưng kiểu đó phải tuân theo SomeProtocoltôi có thể làm:

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

Nhưng có cách nào để thêm một ràng buộc kiểu cho nhiều giao thức không?

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

}

Những thứ tương tự sử dụng dấu phẩy, nhưng trong trường hợp này, nó sẽ bắt đầu khai báo một loại khác. Đây là những gì tôi đã thử.

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

Đây là một câu hỏi có liên quan đặc biệt vì các tài liệu Swift không đề cập đến vấn đề này trong chương chung chung ...
Bruno Philipe

Câu trả lời:


241

Bạn có thể sử dụng mệnh đề where cho phép bạn chỉ định bao nhiêu yêu cầu mà bạn muốn (tất cả đều phải được thực hiện) được phân tách bằng dấu phẩy

Swift 2:

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

Swift 3 & 4:

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

hoặc mệnh đề where mạnh hơn:

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

Tất nhiên bạn có thể sử dụng thành phần giao thức (ví dụ protocol<SomeProtocol, SomeOtherProtocol>:), nhưng nó kém linh hoạt hơn một chút.

Sử dụng wherecho phép bạn đối phó với các trường hợp có nhiều loại liên quan.

Bạn vẫn có thể muốn soạn các giao thức để sử dụng lại ở nhiều nơi hoặc chỉ để đặt cho giao thức tổng hợp một tên có ý nghĩa.

Swift 5:

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

Điều này cảm thấy tự nhiên hơn khi các giao thức bên cạnh đối số.


Geez điều này không hợp lý, nhưng thật tốt khi biết tôi chỉ muốn trở thành một trong những người gửi thư rác cảm ơn cho điều này, đã không nhận ra điều này trong một tháng kể từ khi tôi cần nó.
Mathijs Segers

3
Bất kỳ cách nào để làm điều tương tự với các lớp và cấu trúc trong biểu thức chống chỉ định kiểu? ví dụ <T where T:SomeStruct, T:AnotherStruct>? Đối với các lớp, trình biên dịch dường như giải thích điều này như nói rằng "T là một lớp con của cả hai" và đối với các cấu trúc, nó chỉ phàn nàn điều đó "Type 'T' constrained to non-protocol type".
Jarrod Smith

Đối với ví dụ cụ thể trong thành phần giao thức câu hỏi của OP: s nên là phương thức thích hợp hơn: giải pháp ở trên là hợp lệ, nhưng, imho, không cần thiết phải cắt chữ ký hàm. Ngoài ra, sử dụng thành phần giao thức như, ví dụ, ràng buộc kiểu, vẫn cho phép bạn sử dụng wheremệnh đề cho loại bổ sung / cách sử dụng khác, ví dụ func someFunc<U, T: protocol<SomeProtocol, SomeOtherProtocol> where T.SubType == U>(arg: T, arg2: U) { ... }cho kiểu chữ SubTypetrong ví dụ SomeProtocol.
dfri

1
Có vẻ như điều này không được chấp nhận trong swift3 và khuyên bạn nên sử dụng: func someFunc <T> (arg: T) trong đó T: someProtatio, T:
someOtherProtatio

2
Có cách nào để nói nhanh rằng T cần phải thuộc một loại đối tượng nhất định VÀ thực hiện một giao thức nhất định không?
Georg

73

Bạn có hai khả năng:

  1. Bạn sử dụng mệnh đề where như được chỉ ra trong câu trả lời của Jiaaro:

    func someFunc<T where T : SomeProtocol, T : SomeOtherProtocol>(arg: T) {
        // do stuff
    }
  2. Bạn sử dụng loại thành phần giao thức :

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

2
Imo là giải pháp thứ hai đẹp hơn, tôi sẽ trả lời cho câu trả lời này, nó cũng hoàn thiện hơn khi trình bày hai tùy chọn
Mathijs Segers

2
Số 2 là số duy nhất phù hợp với tôi theo Swift 2 khi khai báo a typealias. Cảm ơn!
Bruno Philipe

19

Sự phát triển của Swift 3.0 mang đến một số thay đổi. Hai lựa chọn của chúng tôi bây giờ trông hơi khác nhau.

Sử dụng wheremệnh đề trong Swift 3.0:

Các wherekhoản bây giờ đã chuyển đến cuối của một chữ ký chức năng để cải thiện khả năng đọc. Vì vậy, nhiều kế thừa giao thức bây giờ trông như thế này:

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

}

Sử dụng protocol<>cấu trúc trong Swift 3.0:

Thành phần sử dụng các protocol<>cấu trúc đang bị phản đối. Trước protocol<SomeProtocol, SomeOtherProtocol>đây trông như thế này:

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

}

Người giới thiệu.

Thông tin thêm về các thay đổi dành cho wheretại đây: https://github.com/apple/swift-evolution/blob/master/proposeals/0081-move-where-expression.md

Và, nhiều hơn về các thay đổi cho giao thức <> xây dựng có tại đây: https://github.com/apple/swift-evolution/blob/master/proposeals/0095-any-as-existential.md


13

Swift 3 cung cấp tối đa 3 cách khác nhau để khai báo chức năng của bạn.

protocol SomeProtocol {
    /* ... */
}

protocol SomeOtherProtocol {
    /* ... */        
}

1. Sử dụng &toán tử

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

2. Sử dụng wheremệnh đề

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

3. Sử dụng wheremệnh đề và &toán tử

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

Cũng lưu ý rằng bạn có thể sử dụng typealiasđể rút ngắn khai báo hàm.

typealias RequiredProtocols = SomeProtocol & SomeOtherProtocol

func someFunc<T: RequiredProtocols>(arg: T) {
    /* ... */   
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.