2015-01-24 20 views
45

Tôi có một giao thức mà tôi định nghĩa như sau:Swift: Kiểm tra xem loại generic phù hợp với giao thức

protocol MyProtocol { 
    ... 
} 

Tôi cũng có một cấu trúc chung:

struct MyStruct <T> { 
    ... 
} 

Cuối cùng tôi có một hàm tổng quát:

func myFunc <T> (s: MyStruct<T>) -> T? { 
    ... 
} 

Tôi muốn kiểm tra bên trong hàm nếu kiểu T phù hợp với MyProtocol. Về cơ bản tôi muốn để có thể làm (~ giả):

let conforms = T.self is MyProtocol 

Nhưng điều này ném một lỗi biên dịch:

error: cannot downcast from 'T.Type' to [email protected] protocol type 'MyProtocol' 
    let conforms = T.self is MyProtocol 
        ~~~~~~^~~~~~~~~~~ 

Tôi cũng đã cố gắng biến thể, như T.self is MyProtocol.self, T is MyProtocol, và sử dụng == thay vì is. Cho đến nay tôi đã không nhận được bất cứ nơi nào. Ý tưởng nào?

Trả lời

4

Bạn cần khai báo các giao thức như @objc:

@objc protocol MyProtocol { 
    ... 
} 

Từ cuốn sách "The Swift Ngôn ngữ lập trình" của Apple:

You can check for protocol conformance only if your protocol is marked with the @objc attribute, as seen for the HasArea protocol above. This attribute indicates that the protocol should be exposed to Objective-C code and is described in Using Swift with Cocoa and Objective-C. Even if you are not interoperating with Objective-C, you need to mark your protocols with the @objc attribute if you want to be able to check for protocol conformance.

Note also that @objc protocols can be adopted only by classes, and not by structures or enumerations. If you mark your protocol as @objc in order to check for conformance, you will be able to apply that protocol only to class types.

+0

Ngay cả với điều này, tôi vẫn nhận được lỗi tương tự. Giao thức '@objc MyProtocol {} struct MyStruct {} func myFunc (s: MyStruct ) -> T? { cho phép conforms = T.self là MyProtocol } ' – Alex

+1

@Alex, bạn cần phải tạo trường hợp kiểu T trước khi bạn có thể kiểm tra sự phù hợp của giao thức (như tôi biết) Nếu bạn cần loại T phải là loại duy nhất phù hợp với MyProtocol, bạn có thể chỉ định nó: 'func myFunc (...) -> T?' – rabbitinspace

26

Câu trả lời đơn giản nhất là: không làm điều đó. Sử dụng quá tải và ràng buộc thay vào đó, và xác định tất cả mọi thứ lên phía trước tại thời gian biên dịch chứ không phải là thử nghiệm công cụ động trong thời gian chạy. Thời gian chạy kiểm tra kiểu và thời gian biên dịch giống như bít tết và kem - cả hai đều tốt đẹp nhưng trộn chúng hơi lạ.

Hãy xem xét một cái gì đó như thế này:

protocol MyProtocol { } 

struct MyStruct <T> { let val: T } 

func myFunc<T: MyProtocol>(s: MyStruct<T>) -> T? { 
    return s.val 
} 

func myFunc<T>(s: MyStruct<T>) -> T? { 
    return nil 
} 

struct S1: MyProtocol { } 
struct S2 { } 

let m1 = MyStruct(val: S1()) 
let m2 = MyStruct(val: S2()) 

myFunc(m1) // returns an instance of S1 
myFunc(m2) // returns nil, because S2 doesn't implement MyProtocol 

Nhược điểm là, bạn không thể thiết lập tự động nếu T hỗ trợ một giao thức trong thời gian chạy:

let o: Any = S1() 
let m3 = MyStruct(val: o) 
myFunc(m3) // will return nil even though o 
      // does actually implement MyProtocol 

Nhưng, trong tất cả sự trung thực, bạn có thực sự cần phải làm điều đó bên trong chức năng chung của bạn? Nếu bạn không chắc chắn loại thực sự là gì, tùy chọn tốt hơn có thể là tìm ra phía trước thay vì trì hoãn nó về sau và thúc đẩy nó bên trong một hàm chung để tìm hiểu.

+11

+1, câu trả lời hay. Đặc biệt được hưởng "_Runtime loại kiểm tra và thời gian biên dịch generics giống như bít tết và kem - cả hai đều tốt đẹp nhưng trộn chúng là một chút lạ._" – Stuart

+5

Vâng ... ngoại trừ các trường hợp cạnh nơi quá tải và khó khăn sẽ không thực hiện công việc. Hãy xem xét một phương thức mở rộng thực hiện một giao thức, 'JSONEncodable', yêu cầu' init (json: JSON) ném'. Chúng ta muốn 'Array' thực hiện' JSONEncodable', nhưng chỉ khi các phần tử của nó cũng là 'JSONEncodable'. Chúng ta không thể kết hợp một mệnh đề thừa kế với các ràng buộc, vì vậy chúng ta phải sử dụng một kiểu kiểm tra nào đó bên trong việc thực thi 'init', và có thể ném một lỗi nếu' Element' không phải là 'JSONEncodable'. Đáng buồn thay, điều này dường như không thể là AFAICT. –

+0

Tôi nên thêm rằng câu hỏi hóc búa ở trên có thể được giải quyết bằng cách sử dụng một loại trung gian như một thunk, nhưng đó là một giải pháp khá không phù hợp. –

42

Một hơi muộn nhưng bạn có thể kiểm tra nếu một cái gì đó phản ứng với giao thức với as ? kiểm tra:

if let currentVC = myViewController as? MyCustomProtocol { 
    //currentVC responds to the MyCustomProtocol protocol =] 
} 

EDIT: ngắn hơn một chút:

if let _=self as? MyProtocol { 
// match 
} 

Và sử dụng một người bảo vệ

guard let _=self as? MyProtocol else { 
    // doesn't match 
    return 
} 
10

bạn cũng có thể tận dụng swift's switch case pattern matching, nếu bạn muốn xử lý nhiều trường hợp của ty pe T:

func myFunc<T>(s: MyStruct<T>) -> T? { 
    switch s { 
    case let sType as MyProtocol: 
     // do MyProtocol specific stuff here, using sType 
    default: 
     //this does not conform to MyProtocol 
    ... 
    } 
} 
22

tôi phải nói @ Alex muốn kiểm tra xem T loại phù hợp với giao thức chứ không phải là s.Và một số người trả lời không thấy rõ ràng.

Kiểm tra T loại phù hợp với giao thức như thế này:

if let _ = T.self as? MyProtocol.Type { 
    // T conform MyProtocol 
} 

hoặc

if T.self is MyProtocol.Type { 
    // T conform MyProtocol 
} 
+0

Đây là câu trả lời đúng. Cảm ơn! – RyanM

Các vấn đề liên quan