Có. Nhưng câu trả lời là hơi lạ. Phần đầu tiên làm cho một số lượng phong nha của cảm giác; phần thứ hai hoàn toàn kỳ lạ. Hãy đi qua nó.
struct Generic<T> {
var args: T
func display() {
print(Printer.print(args))
}
}
Quá tải chính xác để chọn print
được quyết định trong thời gian biên dịch chứ không phải thời gian chạy. Đây là điều làm người ta bối rối nhất. Họ muốn đối xử với Swift như JavaScript, nơi mọi thứ đều năng động. Swift thích tĩnh vì sau đó nó có thể đảm bảo các loại của bạn là đúng và nó có thể làm rất nhiều tối ưu (và Swift yêu thích để tối ưu hóa trình biên dịch). Vì vậy, thời gian biên dịch, loại nào là args
? Vâng, đó là T
. Có phải T
được gọi là Printable
? Không có nó không phải là. Vì vậy, nó sử dụng phiên bản không Printable
.
Nhưng khi Swift chuyên Generic
sử dụng PrintableObj
, không biết tại thời điểm đó là Printable
? Trình biên dịch có thể tạo phiên bản khác của display
tại thời điểm đó không? Có, nếu chúng ta biết tại thời gian biên dịch mỗi người gọi có thể tồn tại của chức năng này, và rằng không ai trong số họ sẽ được mở rộng để được Printable
(có thể xảy ra trong một mô-đun hoàn toàn khác nhau). Thật khó để giải quyết vấn đề này mà không tạo ra nhiều trường hợp góc kỳ lạ (ví dụ như những thứ bên trong hoạt động khác với những thứ công khai) và không buộc Swift chủ động tạo ra mọi phiên bản có thể của display
mà người gọi trong tương lai có thể yêu cầu. Swift có thể cải thiện kịp thời, nhưng đây là một vấn đề khó khăn mà tôi nghĩ. (Swift đã bị giảm một số hiệu suất để các generics công cộng có thể được chuyên biệt mà không cần truy cập vào mã nguồn ban đầu. Điều này sẽ làm cho vấn đề đó thậm chí còn phức tạp hơn.)
OK, vì vậy chúng tôi nhận được điều đó. T
không phải là Printable
. Nhưng nếu chúng ta có loại là rõ ràng Printable
mà chúng ta đã biết lúc biên dịch và sống bên trong hàm này? Nó sẽ làm việc sau đó?
func display() {
if let p = args as? Printable {
print(Printer.print(p))
} else {
print(Printer.print(args))
}
}
Oh rất gần ... nhưng không hoàn toàn. Điều này gần như hoạt động. if-let
thực sự thực hiện chính xác những gì bạn muốn. p
được chỉ định. Đó là Printable
. Nhưng nó vẫn gọi hàm không in được. ?!?!?!?!
Đây là nơi mà cá nhân tôi nghĩ Swift hiện đang bị hỏng và có hy vọng cao sẽ được sửa. Nó thậm chí có thể là một lỗi. Vấn đề là Printable
chính nó không phù hợp với Printable
. Yeah, tôi cũng không hiểu, nhưng có bạn đi. Vì vậy, chúng tôi cần phải làm một cái gì đó mà không phù hợp với Printable
để có được tình trạng quá tải phù hợp. Như thường lệ, type erasers để giải cứu.
struct AnyPrintable: Printable {
let value: Printable
}
struct Generic<T> {
var args: T
func display() {
if let p = args as? Printable {
print(Printer.print(AnyPrintable(value: p)))
} else {
print(Printer.print(args))
}
}
}
Và điều này sẽ in theo cách bạn muốn. (Trên giả định rằng Printable
đòi hỏi một số phương pháp, bạn muốn chỉ cần thêm những phương pháp để các AnyPrintable
loại tẩy.)
Tất nhiên câu trả lời đúng không là sử dụng quá tải chung cách này trong Printer
. Nó chỉ là cách quá khó hiểu và mong manh. Trông nó thật đẹp, nhưng nó luôn luôn nổ tung.
Cảm ơn bạn rất nhiều vì thông tin này. Có vẻ phức tạp để phù hợp với những gì tôi đang cố gắng làm ngay bây giờ. https://github.com/RahulKatariya/Reactofire/blob/develop/ReactofireTests/Models/GenericResponse/_GenericResponse.swift .. Tôi đang cố gắng thực hiện các bài kiểm tra GenericResponseString và GenericResponsePerson. –
Chúc may mắn… Tôi đã làm rất nhiều thử nghiệm trong các loại phương pháp siêu thông minh/kỳ diệu này đến các vấn đề cơ bản. Cá nhân tôi thấy rằng một vài dòng mã đơn giản, ngay cả khi thỉnh thoảng bạn phải lặp lại một vài dòng tẻ nhạt ở đây hoặc ở đó, hoạt động tốt hơn nhiều, đặc biệt là khi đến lúc gỡ lỗi (tôi chỉ cần 'bảo vệ-cho' phân tích cú pháp JSON của tôi bây giờ; dễ dàng hơn nhiều). Nó không phải là thú vị, nhưng nó đã làm việc tốt hơn rất nhiều trong các dự án của tôi. Nhưng may mắn nhất cho những người vẫn khám phá biên giới! Đó là niềm vui, nhưng có rất nhiều thất vọng như thế này. –