2016-02-16 16 views
7

Es gibt ein Protokoll Printable und ein Struct Drucker von einem Drittanbieter.Generische Methode Override funktioniert nicht in Swift

protocol Printable {} 

struct Printer { 

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

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

} 

Jetzt mache ich ein generisches

struct Generic<T> { 

    var args: T 

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

} 

und zwei structs

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

Wenn ich die Anzeigefunktionen auf beide nennen.

obj.display() 

Displays T

printableObj.display() 

zeigt T, aber ich will es "Druck"

Eine Lösung i ist mit zwei verschiedenen Generika denken kann, drucken

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

Gibt es eine andere Lösung, ohne die Printable p zu ändern Rotocol und Printer struct.

Antwort

1

Ja. Aber die Antwort ist ein bisschen komisch. Der erste Teil macht eine vernünftige Menge an Sinn; Der zweite Teil ist einfach total komisch. Lass uns hindurchgehen.

struct Generic<T> { 
    var args: T 
    func display() { 
     print(Printer.print(args)) 
    } 
} 

Die richtige Überlastung für print zu wählen ist bei der Kompilierung entschieden, nicht Runtime. Das ist die Sache, die die Menschen am meisten verwirrt. Sie möchten Swift wie JavaScript behandeln, wo alles dynamisch ist. Swift ist gerne statisch, weil es dann sicherstellen kann, dass Ihre Typen richtig sind und viele Optimierungen durchführen können (und Swift liebt, um Compiler-Optimierungen durchzuführen). Also, Kompilierzeit, welcher Typ ist args? Nun, es ist T. Ist T bekannt als Printable? Nein ist es nicht. So verwendet es die nicht Printable Version.

Aber wenn Swift spezialisiert Generic mit PrintableObj, weiß es nicht an diesem Punkt, dass es Printable ist? Konnte der Compiler zu diesem Zeitpunkt keine andere Version von display erstellen? Ja, wenn wir zur Kompilierzeit jeden Aufrufer kennen würden, der jemals von dieser Funktion existieren würde, und dass keiner von ihnen jemals zu Printable erweitert würde (was in einem komplett anderen Modul passieren könnte). Es ist schwierig, dies zu lösen, ohne viele seltsame Eckenfälle zu erzeugen (wo sich beispielsweise interne Dinge anders verhalten als öffentliche Dinge), ohne Swift dazu zu zwingen, proaktiv jede mögliche Version von display zu erzeugen, die von irgendeinem zukünftigen Anrufer benötigt wird. Swift mag sich mit der Zeit verbessern, aber das ist ein schweres Problem, denke ich. (Swift erleidet bereits Leistungseinbußen, sodass öffentliche Generika ohne Zugriff auf den ursprünglichen Quellcode spezialisiert werden können. Dies würde dieses Problem noch komplizierter machen.)

OK, also bekommen wir das. T ist nicht Printable. Aber was, wenn wir einen Typ hatten, der war eindeutig Printable, dass wir zur Kompilierzeit wussten und in dieser Funktion lebten? Würde es dann funktionieren?

func display() { 
    if let p = args as? Printable { 
     print(Printer.print(p)) 
    } else { 
     print(Printer.print(args)) 
    } 
} 

Oh so nah ... aber nicht ganz. Diese fast funktioniert. Die if-let macht eigentlich genau das, was Sie wollen. wird zugewiesen. Es ist Printable. Aber es ruft immer noch die nicht druckbare Funktion auf. ?!?!?!?!

Dies ist ein Ort, den ich persönlich denke, dass Swift gerade gerade gebrochen ist und große Hoffnungen haben, dass es behoben wird. Es könnte sogar ein Fehler sein. Das Problem ist, dass Printable selbst nicht Printable entspricht. Ja, ich verstehe es auch nicht, aber da gehst du hin. Also müssen wir etwas machen, das Printable entspricht, um die richtige Überlastung zu bekommen. Wie immer, type erasers zur Rettung.

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)) 
     } 
    } 
} 

Und dies wird so drucken, wie Sie wollten. (Unter der Annahme, dass Printable einige Methoden erfordern, würden Sie nur diese Methoden zum AnyPrintable Art Radiergummi hinzufügen.)

Natürlich ist die richtige Antwort ist nicht generisch Überlastungen auf diese Weise in Printer zu verwenden. Es ist einfach viel zu verwirrend und zerbrechlich. Es sieht so gut aus, aber es explodiert die ganze Zeit.

+0

Vielen Dank für diese Information. Es sieht komplex aus, um in das zu passen, was ich gerade versuche. https://github.com/RahulKatariya/Reactofire/blob/develop/ReactofireTests/Models/GenericResponse/_GenericResponse.swift .. Ich versuche, GenericResponseString und GenericResponsePerson Tests bestanden zu machen. –

+0

Viel Glück ... Ich habe viel experimentiert mit diesen super-cleveren/magischen Ansätzen zu grundlegenden Problemen. Persönlich fand ich, dass ein paar Zeilen einfachen Codes, auch wenn Sie gelegentlich ein paar mühsame Zeilen hier oder dort wiederholen müssen, viel besser funktionieren, besonders wenn es Zeit ist zu debuggen (ich bewache einfach mein JSON Parsing jetzt; so viel einfacher). Es ist nicht so aufregend, aber es hat viel besser in meinen Projekten funktioniert. Aber viel Glück für diejenigen, die immer noch die Grenze erkunden! Es macht Spaß, aber es gibt eine Menge Frustration wie diese. –

1

Meiner Meinung nach, ist die einzige Option, die Sie haben - ist mit Typ-Casting in "print()" Funktion

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

oder nicht-generische Variante if-else verwenden

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

Danke. Aber dieser Code stammt aus einer anderen Quelle und ich kann ihn nicht ändern. Ich kann nur Änderungen in Generic vornehmen. –

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

Danke. Aber dieser Code stammt aus einer anderen Quelle und ich kann ihn nicht ändern. Ich kann nur Änderungen in Generic vornehmen. –