2016-02-16 26 views
7

Có một giao thức Có thể in và Máy in cấu trúc từ Bên thứ ba.Ghi đè phương pháp chung không hoạt động trong swift

protocol Printable {} 

struct Printer { 

    static func print<T>(object: T) -> String { 
     return "T" 
    } 

    static func print<T: Printable>(object: T) -> String { 
     return "Printable" 
    } 

} 

Bây giờ tôi đang làm cho một generic

struct Generic<T> { 

    var args: T 

    func display() { 
     print(Printer.print(args)) 
    } 

} 

và hai cấu trúc

struct Obj {} 
struct PrintableObj: Printable {} 
var obj = Generic(args: Obj()) 
var printableObj = Generic(args: PrintableObj()) 

Khi tôi gọi các chức năng hiển thị trên cả hai.

obj.display() 

màn T

printableObj.display() 

màn T nhưng tôi muốn nó in "in"

Một giải pháp tôi có thể nghĩ đến là có hai Generics khác nhau

struct Generic<T> 
struct PrintableGeneric<T: Printable> 

Có giải pháp nào khác mà không thay đổi p có thể in rotocol và Cấu trúc máy in.

Trả lời

1

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 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.

+0

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. –

+0

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. –

1

Để tâm trí của tôi, lựa chọn duy nhất mà bạn có - là sử dụng if-else với loại đúc trong "print()" chức năng

static func print<T>(object: T) -> String { 
    if let _ = object as? Printable { 
     return "Printable" 
    } 
    return "T" 
} 

hoặc không generic biến

static func print(object: Any) -> String { 
    if let _ = object as? Printable { 
     return "Printable" 
    } 
    return "T" 
} 
+0

Cảm ơn. Nhưng mã đó là từ một nguồn khác và tôi không thể sửa đổi nó. Tôi chỉ có thể thực hiện thay đổi trong Chung. –

2
static func print<T>(object: T) -> String { 
    if object is Printable { 
     return "Printable" 
    } else { 
     return "T" 
    } 
} 
+0

Cảm ơn. Nhưng mã đó là từ một nguồn khác và tôi không thể sửa đổi nó. Tôi chỉ có thể thực hiện thay đổi trong Chung. –

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